diff options
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/compiler.pri | 27 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 2605 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 451 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 56 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 621 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_masm.cpp | 1466 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_masm_p.h | 939 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 1089 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth_p.h | 201 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.cpp | 440 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.h | 165 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_util_p.h | 87 | ||||
-rw-r--r-- | src/qml/compiler/qv4jsir.cpp | 1024 | ||||
-rw-r--r-- | src/qml/compiler/qv4jsir_p.h | 890 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa.cpp | 2122 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa_p.h | 127 | ||||
-rw-r--r-- | src/qml/compiler/qv4vme_moth.cpp | 596 | ||||
-rw-r--r-- | src/qml/compiler/qv4vme_moth_p.h | 76 |
18 files changed, 12982 insertions, 0 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri new file mode 100644 index 0000000000..61578fd011 --- /dev/null +++ b/src/qml/compiler/compiler.pri @@ -0,0 +1,27 @@ +include(../../3rdparty/masm/masm-defs.pri) + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +HEADERS += \ + $$PWD/qv4codegen_p.h \ + $$PWD/qv4isel_masm_p.h \ + $$PWD/qv4isel_p.h \ + $$PWD/qv4jsir_p.h \ + $$PWD/qv4vme_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4isel_util_p.h \ + $$PWD/qv4ssa_p.h + +SOURCES += \ + $$PWD/qv4codegen.cpp \ + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4isel_masm.cpp \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4isel_p.cpp \ + $$PWD/qv4jsir.cpp \ + $$PWD/qv4ssa.cpp \ + $$PWD/qv4vme_moth.cpp + +include(../../3rdparty/masm/masm.pri) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp new file mode 100644 index 0000000000..d0c43c8f56 --- /dev/null +++ b/src/qml/compiler/qv4codegen.cpp @@ -0,0 +1,2605 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4codegen_p.h" +#include "qv4util_p.h" +#include "qv4debugging_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 <qv4runtime_p.h> +#include <qv4context_p.h> +#include <cmath> +#include <iostream> +#include <cassert> + +#ifdef CONST +#undef CONST +#endif + +#define QV4_NO_LIVENESS +#undef SHOW_SSA + +using namespace QQmlJS; +using namespace AST; + +class Codegen::ScanFunctions: Visitor +{ + typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode) + : _cg(cg) + , _sourceCode(sourceCode) + , _env(0) + , _inFuncBody(false) + , _allowFuncDecls(true) + { + } + + void operator()(Node *node) + { + if (node) + node->accept(this); + } + + inline void enterEnvironment(Node *node) + { + Environment *e = _cg->newEnvironment(node, _env); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _envStack.append(e); + _env = e; + } + + inline void leaveEnvironment() + { + _envStack.pop(); + _env = _envStack.isEmpty() ? 0 : _envStack.top(); + } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void 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 == QStringLiteral("use strict")) { + _env->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } + } + + void checkName(const QStringRef &name, const SourceLocation &loc) + { + if (_env->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, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); + } + } + } + void checkForArguments(AST::FormalParameterList *parameters) + { + while (parameters) { + if (parameters->name == QStringLiteral("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + parameters = parameters->next; + } + } + + virtual bool visit(Program *ast) + { + enterEnvironment(ast); + checkDirectivePrologue(ast->elements); + return true; + } + + virtual void endVisit(Program *) + { + leaveEnvironment(); + } + + virtual bool visit(CallExpression *ast) + { + if (! _env->hasDirectEval) { + if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { + if (id->name == QStringLiteral("eval")) { + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _env->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(NewMemberExpression *ast) + { + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool 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; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + return true; + } + + virtual bool visit(VariableDeclaration *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + return true; + } + + virtual bool visit(IdentifierExpression *ast) + { + checkName(ast->name, ast->identifierToken); + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + return true; + } + + virtual bool visit(ExpressionStatement *ast) + { + if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QCoreApplication::translate("qv4codegen", "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) == QStringLiteral("function")) { + _cg->throwSyntaxError(firstToken, QCoreApplication::translate("qv4codegen", "unexpected token")); + } + } + return true; + } + + virtual bool visit(FunctionExpression *ast) + { + enterFunction(ast, /*enterName*/ false); + return true; + } + + void enterFunction(FunctionExpression *ast, bool enterName, bool isExpression = true) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); + } + + virtual void endVisit(FunctionExpression *) + { + leaveEnvironment(); + } + + virtual bool visit(ObjectLiteral *ast) + { + int argc = 0; + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + ++argc; + if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) + ++argc; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; + } + + virtual bool visit(PropertyGetterSetter *ast) + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); + return true; + } + + virtual void endVisit(PropertyGetterSetter *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionDeclaration *ast) + { + enterFunction(ast, /*enterName*/ true, /*isExpression */false); + return true; + } + + virtual void endVisit(FunctionDeclaration *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionBody *ast) + { + TemporaryBoolAssignment inFuncBody(_inFuncBody, true); + Node::accept(ast->elements, this); + return false; + } + + virtual bool visit(WithStatement *ast) + { + if (_env->isStrict) { + _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); + return false; + } + + return true; + } + + virtual bool visit(IfStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->ok, this); + Node::accept(ast->ko, this); + + return false; + } + + virtual bool visit(WhileStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; + } + + virtual bool visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; + } + +private: + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) + { + bool wasStrict = false; + if (_env) { + _env->hasNestedFunctions = true; + // The identifier of a function expression cannot be referenced from the enclosing environment. + if (expr) + _env->enter(name, Environment::FunctionDefinition, expr); + if (name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _env->isStrict; + } + + enterEnvironment(ast); + checkForArguments(formals); + + _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _env->formals = formals; + + if (body) + checkDirectivePrologue(body->elements); + + if (wasStrict || _env->isStrict) { + QStringList args; + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (args.contains(arg)) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); + args += arg; + } + } + } + +private: // fields: + Codegen *_cg; + const QString _sourceCode; + Environment *_env; + QStack<Environment *> _envStack; + + bool _inFuncBody; + bool _allowFuncDecls; +}; + +Codegen::Codegen(QV4::ExecutionContext *context, bool strict) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(context) + , _strictMode(strict) + , _errorHandler(0) +{ +} + +Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(0) + , _strictMode(strictMode) + , _errorHandler(errorHandler) +{ +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + Program *node, + V4IR::Module *module, + Mode mode, + const QStringList &inheritedLocals) +{ + assert(node); + + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + scan(node); + + V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, + node->elements, mode, inheritedLocals); + qDeleteAll(_envMap); + _envMap.clear(); + + return globalCode; +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module) +{ + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + // fake a global environment + scan.enterEnvironment(0); + scan(ast); + scan.leaveEnvironment(); + + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + + qDeleteAll(_envMap); + _envMap.clear(); + + return function; +} + + +void Codegen::enterEnvironment(Node *node) +{ + _env = _envMap.value(node); + assert(_env); +} + +void Codegen::leaveEnvironment() +{ + assert(_env); + _env = _env->parent; +} + +void Codegen::enterLoop(Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock) +{ + if (startBlock) + startBlock->markAsGroupStart(); + _loop = new Loop(node, startBlock, 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; +} + +V4IR::Expr *Codegen::member(V4IR::Expr *base, const QString *name) +{ + if (base->asTemp() /*|| base->asName()*/) + return _block->MEMBER(base->asTemp(), name); + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + return _block->MEMBER(_block->TEMP(t), name); + } +} + +V4IR::Expr *Codegen::subscript(V4IR::Expr *base, V4IR::Expr *index) +{ + if (! base->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + if (! index->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), index); + index = _block->TEMP(t); + } + + assert(base->asTemp() && index->asTemp()); + return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); +} + +V4IR::Expr *Codegen::argument(V4IR::Expr *expr) +{ + if (expr && ! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +// keeps references alive, converts other expressions to temps +V4IR::Expr *Codegen::reference(V4IR::Expr *expr) +{ + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +V4IR::Expr *Codegen::unop(V4IR::AluOp op, V4IR::Expr *expr) +{ + Q_ASSERT(op != V4IR::OpIncrement); + Q_ASSERT(op != V4IR::OpDecrement); + + if (V4IR::Const *c = expr->asConst()) { + if (c->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpNot: + return _block->CONST(V4IR::BoolType, !c->value); + case V4IR::OpUMinus: + return _block->CONST(V4IR::NumberType, -c->value); + case V4IR::OpUPlus: + return expr; + case V4IR::OpCompl: + return _block->CONST(V4IR::NumberType, ~QV4::Value::toInt32(c->value)); + case V4IR::OpIncrement: + return _block->CONST(V4IR::NumberType, c->value + 1); + case V4IR::OpDecrement: + return _block->CONST(V4IR::NumberType, c->value - 1); + default: + break; + } + } + } + if (! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + assert(expr->asTemp()); + return _block->UNOP(op, expr->asTemp()); +} + +V4IR::Expr *Codegen::binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right) +{ + if (V4IR::Const *c1 = left->asConst()) { + if (V4IR::Const *c2 = right->asConst()) { + if (c1->type == V4IR::NumberType && c2->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpAdd: return _block->CONST(V4IR::NumberType, c1->value + c2->value); + case V4IR::OpAnd: return _block->CONST(V4IR::BoolType, c1->value ? c2->value : 0); + case V4IR::OpBitAnd: return _block->CONST(V4IR::NumberType, int(c1->value) & int(c2->value)); + case V4IR::OpBitOr: return _block->CONST(V4IR::NumberType, int(c1->value) | int(c2->value)); + case V4IR::OpBitXor: return _block->CONST(V4IR::NumberType, int(c1->value) ^ int(c2->value)); + case V4IR::OpDiv: return _block->CONST(V4IR::NumberType, c1->value / c2->value); + case V4IR::OpEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpStrictEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpStrictNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpGe: return _block->CONST(V4IR::BoolType, c1->value >= c2->value); + case V4IR::OpGt: return _block->CONST(V4IR::BoolType, c1->value > c2->value); + case V4IR::OpLe: return _block->CONST(V4IR::BoolType, c1->value <= c2->value); + case V4IR::OpLt: return _block->CONST(V4IR::BoolType, c1->value < c2->value); + case V4IR::OpLShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) << (QV4::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpMod: return _block->CONST(V4IR::NumberType, std::fmod(c1->value, c2->value)); + case V4IR::OpMul: return _block->CONST(V4IR::NumberType, c1->value * c2->value); + case V4IR::OpOr: return _block->CONST(V4IR::NumberType, c1->value ? c1->value : c2->value); + case V4IR::OpRShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpSub: return _block->CONST(V4IR::NumberType, c1->value - c2->value); + case V4IR::OpURShift: return _block->CONST(V4IR::NumberType,QV4::Value::toUInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); + + case V4IR::OpInstanceof: + case V4IR::OpIn: + break; + + case V4IR::OpIfTrue: // unary ops + case V4IR::OpNot: + case V4IR::OpUMinus: + case V4IR::OpUPlus: + case V4IR::OpCompl: + case V4IR::OpIncrement: + case V4IR::OpDecrement: + case V4IR::OpInvalid: + break; + } + } + } + } else if (op == V4IR::OpAdd) { + if (V4IR::String *s1 = left->asString()) { + if (V4IR::String *s2 = right->asString()) { + return _block->STRING(_function->newString(*s1->value + *s2->value)); + } + } + } + + if (!left->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + if (!right->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + right = _block->TEMP(t); + } + + assert(left->asTemp()); + assert(right->asTemp()); + + return _block->BINOP(op, left, right); +} + +V4IR::Expr *Codegen::call(V4IR::Expr *base, V4IR::ExprList *args) +{ + base = reference(base); + return _block->CALL(base, args); +} + +void Codegen::move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op) +{ + assert(target->isLValue()); + + // TODO: verify the rest of the function for when op == OpInvalid + if (op != V4IR::OpInvalid) { + move(target, binop(op, target, source), V4IR::OpInvalid); + return; + } + + if (!source->asTemp() && !source->asConst() && (op != V4IR::OpInvalid || ! target->asTemp())) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + if (source->asConst() && (!target->asTemp() || op != V4IR::OpInvalid)) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + + _block->MOVE(target, source, op); +} + +void Codegen::cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (! (cond->asTemp() || cond->asBinop())) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), cond); + cond = _block->TEMP(t); + } + _block->CJUMP(cond, iftrue, iffalse); +} + +void Codegen::accept(Node *node) +{ + if (node) + node->accept(this); +} + +void Codegen::statement(Statement *ast) +{ + _block->nextLocation = ast->firstSourceLocation(); + accept(ast); +} + +void Codegen::statement(ExpressionNode *ast) +{ + if (! ast) { + return; + } else { + Result r(nx); + qSwap(_expr, r); + accept(ast); + 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()) { + // there is nothing to do + } else { + unsigned t = _block->newTemp(); + move(_block->TEMP(t), *r); + } + } + } +} + +void Codegen::condition(ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (ast) { + Result r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + cjump(*r, r.iftrue, r.iffalse); + } + } +} + +Codegen::Result Codegen::expression(ExpressionNode *ast) +{ + Result r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +QString Codegen::propertyName(PropertyName *ast) +{ + QString p; + if (ast) { + qSwap(_property, p); + accept(ast); + qSwap(_property, p); + } + return p; +} + +Codegen::Result Codegen::sourceElement(SourceElement *ast) +{ + Result r(nx); + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + 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) + sourceElements(ast->elements); +} + +void Codegen::program(Program *ast) +{ + if (ast) { + sourceElements(ast->elements); + } +} + +void Codegen::sourceElements(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + sourceElement(it->element); + } +} + +void Codegen::variableDeclaration(VariableDeclaration *ast) +{ + V4IR::Expr *initializer = 0; + if (!ast->expression) + return; + Result expr = expression(ast->expression); + assert(expr.code); + initializer = *expr; + + if (! _env->parent || _function->insideWithOrCatch) { + // it's global code. + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); + } else { + const int index = _env->findMember(ast->name.toString()); + assert(index != -1); + move(_block->LOCAL(index, 0), initializer); + } +} + +void Codegen::variableDeclarationList(VariableDeclarationList *ast) +{ + for (VariableDeclarationList *it = ast; it; it = it->next) { + variableDeclaration(it->declaration); + } +} + + +bool Codegen::visit(ArgumentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseBlock *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClauses *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Catch *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(DefaultClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(ElementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Elision *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Finally *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FormalParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FunctionBody *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Program *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyAssignmentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValue *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyGetterSetter *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(SourceElements *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(StatementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiArrayMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImport *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImportList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectInitializer *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiProgram *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiQualifiedId *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclaration *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclarationList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Expression *ast) +{ + statement(ast->left); + accept(ast->right); + return false; +} + +bool Codegen::visit(ArrayLiteral *ast) +{ + V4IR::ExprList *args = 0; + V4IR::ExprList *current = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + Result expr = expression(it->expression); + + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + + V4IR::Expr *exp = *expr; + if (exp->asTemp() || exp->asConst()) { + current->expr = exp; + } else { + unsigned value = _block->newTemp(); + move(_block->TEMP(value), exp); + current->expr = _block->TEMP(value); + } + } + for (Elision *elision = ast->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_array, 0, 0), args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(ArrayMemberExpression *ast) +{ + Result base = expression(ast->base); + Result index = expression(ast->expression); + _expr.code = subscript(*base, *index); + return false; +} + +static V4IR::AluOp baseOp(int op) +{ + switch ((QSOperator::Op) op) { + case QSOperator::InplaceAnd: return V4IR::OpBitAnd; + case QSOperator::InplaceSub: return V4IR::OpSub; + case QSOperator::InplaceDiv: return V4IR::OpDiv; + case QSOperator::InplaceAdd: return V4IR::OpAdd; + case QSOperator::InplaceLeftShift: return V4IR::OpLShift; + case QSOperator::InplaceMod: return V4IR::OpMod; + case QSOperator::InplaceMul: return V4IR::OpMul; + case QSOperator::InplaceOr: return V4IR::OpBitOr; + case QSOperator::InplaceRightShift: return V4IR::OpRShift; + case QSOperator::InplaceURightShift: return V4IR::OpURShift; + case QSOperator::InplaceXor: return V4IR::OpBitXor; + default: return V4IR::OpInvalid; + } +} + +bool Codegen::visit(BinaryExpression *ast) +{ + if (ast->op == QSOperator::And) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + condition(ast->left, iftrue, _expr.iffalse); + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned r = _block->newTemp(); + + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), iftrue, endif); + _block = iftrue; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _expr.code = _block->TEMP(r); + _block = endif; + } + return false; + } else if (ast->op == QSOperator::Or) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + condition(ast->left, _expr.iftrue, iffalse); + _block = iffalse; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), endif, iffalse); + _block = iffalse; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _block = endif; + _expr.code = _block->TEMP(r); + } + return false; + } + + V4IR::Expr* left = *expression(ast->left); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + break; + + case QSOperator::Assign: { + V4IR::Expr* right = *expression(ast->right); + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); + + 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); + } + break; + } + + 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: { + V4IR::Expr* right = *expression(ast->right); + if (!left->isLValue()) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right, baseOp(ast->op)); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = left; + } + break; + } + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + if (_expr.accept(cx)) { + cjump(binop(V4IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + } + break; + } + + case QSOperator::Add: + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::Div: + case QSOperator::LShift: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::RShift: + case QSOperator::Sub: + case QSOperator::URShift: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + break; + } + + } // switch + + return false; +} + +bool Codegen::visit(CallExpression *ast) +{ + Result base = expression(ast->base); + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New<V4IR::ExprList>(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = call(*base, args); + return false; +} + +bool Codegen::visit(ConditionalExpression *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned t = _block->newTemp(); + + condition(ast->expression, iftrue, iffalse); + + _block = iftrue; + move(_block->TEMP(t), *expression(ast->ok)); + _block->JUMP(endif); + + _block = iffalse; + move(_block->TEMP(t), *expression(ast->ko)); + _block->JUMP(endif); + + _block = endif; + + _expr.code = _block->TEMP(t); + + return false; +} + +bool Codegen::visit(DeleteExpression *ast) +{ + V4IR::Expr* expr = *expression(ast->expression); + // Temporaries cannot be deleted + if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { + // Trying to delete a function argument might throw. + if (_function->isStrict && expr->asTemp()->index < 0) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + _expr.code = _block->CONST(V4IR::BoolType, 0); + return false; + } + if (_function->isStrict && expr->asName()) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + + // [[11.4.1]] Return true if it's not a reference + if (expr->asConst() || expr->asString()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + 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(V4IR::BoolType, 1); + return false; + } + if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(reference(expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + return false; +} + +bool Codegen::visit(FalseLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 0); + } + return false; +} + +bool Codegen::visit(FieldMemberExpression *ast) +{ + Result base = expression(ast->base); + _expr.code = member(*base, _function->newString(ast->name.toString())); + return false; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _expr.code = _block->CLOSURE(function); + return false; +} + +V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) +{ + uint scope = 0; + Environment *e = _env; + V4IR::Function *f = _function; + + while (f && e->parent) { + if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) + break; + int index = e->findMember(name); + assert (index < e->members.size()); + if (index != -1) { + return _block->LOCAL(index, scope); + } + const int argIdx = f->indexOfArgument(&name); + if (argIdx != -1) + return _block->ARG(argIdx, scope); + ++scope; + e = e->parent; + f = f->outer; + } + + if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && _mode != QmlBinding && (!f || f->name != name)) + return _block->GLOBALNAME(name, line, col); + + // global context or with. Lookup by name + return _block->NAME(name, line, col); + +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + return false; +} + +bool Codegen::visit(NestedExpression *ast) +{ + accept(ast->expression); + return false; +} + +bool Codegen::visit(NewExpression *ast) +{ + Result base = expression(ast->expression); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + _expr.code = _block->NEW(expr, 0); + return false; +} + +bool Codegen::visit(NewMemberExpression *ast) +{ + Result base = expression(ast->base); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New<V4IR::ExprList>(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(expr, args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(NotExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), unop(V4IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); + return false; +} + +bool Codegen::visit(NullExpression *) +{ + if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); + else _expr.code = _block->CONST(V4IR::NullType, 0); + + return false; +} + +bool Codegen::visit(NumericLiteral *ast) +{ + if (_expr.accept(cx)) { + if (ast->value) _block->JUMP(_expr.iftrue); + else _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::NumberType, ast->value); + } + return false; +} + +struct ObjectPropertyValue { + V4IR::Expr *value; + V4IR::Function *getter; + V4IR::Function *setter; +}; + +bool Codegen::visit(ObjectLiteral *ast) +{ + QMap<QString, ObjectPropertyValue> valueMap; + + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { + QString name = propertyName(nv->name); + Result value = expression(nv->value); + ObjectPropertyValue &v = valueMap[name]; + if (v.getter || v.setter || (_function->isStrict && v.value)) + throwSyntaxError(nv->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + + valueMap[name].value = *value; + } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { + QString name = propertyName(gs->name); + V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); + ObjectPropertyValue &v = valueMap[name]; + if (v.value || + (gs->type == PropertyGetterSetter::Getter && v.getter) || + (gs->type == PropertyGetterSetter::Setter && v.setter)) + throwSyntaxError(gs->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + if (gs->type == PropertyGetterSetter::Getter) + v.getter = function; + else + v.setter = function; + } else { + Q_UNREACHABLE(); + } + } + + V4IR::ExprList *args = 0; + + if (!valueMap.isEmpty()) { + V4IR::ExprList *current; + for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) { + if (QV4::String(0, it.key()).asArrayIndex() != UINT_MAX) { + ++it; + continue; + } + + if (!args) { + args = _function->New<V4IR::ExprList>(); + current = args; + } else { + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + } + + current->expr = _block->NAME(it.key(), 0, 0); + + if (it->value) { + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->CONST(V4IR::BoolType, true); + + unsigned value = _block->newTemp(); + move(_block->TEMP(value), it->value); + + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(value); + } else { + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->CONST(V4IR::BoolType, false); + + unsigned getter = _block->newTemp(); + unsigned setter = _block->newTemp(); + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(getter); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(setter); + } + + it = valueMap.erase(it); + } + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_object_literal, + ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); + + // What's left are array entries + if (!valueMap.isEmpty()) { + unsigned value = 0; + unsigned getter = 0; + unsigned setter = 0; + for (QMap<QString, ObjectPropertyValue>::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + V4IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + + if (it->value) { + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), it->value); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_property, 0, 0), args)); + } else { + if (!getter) { + getter = _block->newTemp(); + setter = _block->newTemp(); + } + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + current->expr = _block->TEMP(getter); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_getter_setter, 0, 0), args)); + } + } + } + + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(PostDecrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + + if (_expr.accept(nx)) { + move(*expr, binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1))); + } else { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PostIncrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + + if (_expr.accept(nx)) { + move(*expr, binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1))); + } else { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PreDecrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + V4IR::Expr *op = binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1)); + if (_expr.accept(nx)) { + move(*expr, op); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), op); + move(*expr, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(PreIncrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + V4IR::Expr *op = binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1)); + if (_expr.accept(nx)) { + move(*expr, op); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), op); + move(*expr, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(RegExpLiteral *ast) +{ + _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + return false; +} + +bool Codegen::visit(StringLiteral *ast) +{ + _expr.code = _block->STRING(_function->newString(ast->value.toString())); + return false; +} + +bool Codegen::visit(ThisExpression *ast) +{ + _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + return false; +} + +bool Codegen::visit(TildeExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpCompl, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(TrueLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iftrue); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 1); + } + return false; +} + +bool Codegen::visit(TypeOfExpression *ast) +{ + Result expr = expression(ast->expression); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(reference(*expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + return false; +} + +bool Codegen::visit(UnaryMinusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUMinus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(UnaryPlusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUPlus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(VoidExpression *ast) +{ + statement(ast->expression); + _expr.code = _block->CONST(V4IR::UndefinedType, 0); + return false; +} + +bool Codegen::visit(FunctionDeclaration * ast) +{ + if (_mode == QmlBinding) + move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); + _expr.accept(nx); + return false; +} + +V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, Mode mode, + const QStringList &inheritedLocals) +{ + qSwap(_mode, mode); // enter function code. + Loop *loop = 0; + qSwap(_loop, loop); + + ScopeAndFinally *scopeAndFinally = 0; + + enterEnvironment(ast); + V4IR::Function *function = _module->newFunction(name, _function); + function->sourceFile = _fileName; + + V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock); + V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock()); + function->hasDirectEval = _env->hasDirectEval; + function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->isStrict = _env->isStrict; + function->isNamedExpression = _env->isNamedFunctionExpression; + + // variables in global code are properties of the global context object, not locals as with other functions. + if (_mode == FunctionCode) { + unsigned t = 0; + for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { + const QString &local = it.key(); + function->LOCAL(local); + (*it).index = t; + entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(V4IR::UndefinedType, 0)); + ++t; + } + } else { + if (!_env->isStrict) { + foreach (const QString &inheritedLocal, inheritedLocals) { + function->LOCAL(inheritedLocal); + unsigned tempIndex = entryBlock->newTemp(); + Environment::Member member = { Environment::UndefinedMember, + static_cast<int>(tempIndex), 0 }; + _env->members.insert(inheritedLocal, member); + } + } + + V4IR::ExprList *args = 0; + for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { + const QString &local = it.key(); + V4IR::ExprList *next = function->New<V4IR::ExprList>(); + next->expr = entryBlock->NAME(local, 0, 0); + next->next = args; + args = next; + } + if (args) { + V4IR::ExprList *next = function->New<V4IR::ExprList>(); + next->expr = entryBlock->CONST(V4IR::BoolType, (mode == EvalCode || mode == QmlBinding)); + next->next = args; + args = next; + + entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(V4IR::Name::builtin_declare_vars, 0, 0), args)); + } + } + + unsigned returnAddress = entryBlock->newTemp(); + + entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0)); + exitBlock->RET(exitBlock->TEMP(returnAddress)); + V4IR::ExprList *throwArgs = function->New<V4IR::ExprList>(); + throwArgs->expr = throwBlock->TEMP(returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + + for (FormalParameterList *it = formals; it; it = it->next) { + _function->RECEIVE(it->name.toString()); + } + + foreach (const Environment::Member &member, _env->members) { + if (member.function) { + V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, + member.function->body ? member.function->body->elements : 0); + if (! _env->parent) { + move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), + _block->CLOSURE(function)); + } else { + assert(member.index >= 0); + move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); + } + } + } + + sourceElements(body); + + _function->insertBasicBlock(_exitBlock); + + _block->JUMP(_exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + leaveEnvironment(); + + qSwap(_mode, mode); + + return function; +} + +bool Codegen::visit(IdentifierPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(NumericLiteralPropertyName *ast) +{ + _property = QString::number(ast->id, 'g', 16); + return false; +} + +bool Codegen::visit(StringLiteralPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(FunctionSourceElement *ast) +{ + statement(ast->declaration); + return false; +} + +bool Codegen::visit(StatementSourceElement *ast) +{ + statement(ast->statement); + return false; +} + +bool Codegen::visit(Block *ast) +{ + for (StatementList *it = ast->statements; it; it = it->next) { + statement(it->statement); + } + return false; +} + +bool Codegen::visit(BreakStatement *ast) +{ + if (!_loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); + 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) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->breakBlock); + return false; +} + +bool Codegen::visit(ContinueStatement *ast) +{ + 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(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->continueBlock); + return false; +} + +bool Codegen::visit(DebuggerStatement *) +{ + Q_UNIMPLEMENTED(); + return false; +} + +bool Codegen::visit(DoWhileStatement *ast) +{ + V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody); + V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, loopbody, loopend, loopcond); + + _block->JUMP(loopbody); + + _block = loopbody; + statement(ast->statement); + _block->JUMP(loopcond); + + _block = loopcond; + condition(ast->expression, loopbody, loopend); + + _block = loopend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(EmptyStatement *) +{ + return false; +} + +bool Codegen::visit(ExpressionStatement *ast) +{ + if (_mode == EvalCode || _mode == QmlBinding) { + Result e = expression(ast->expression); + if (*e) + move(_block->TEMP(_returnAddress), *e); + } else { + statement(ast->expression); + } + return false; +} + +bool Codegen::visit(ForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, foreachin, foreachend, foreachin); + + int objectToIterateOn = _block->newTemp(); + move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(objectToIterateOn)); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(*expression(ast->initialiser), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(ForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, forcond, forend, forstep); + + statement(ast->initialiser); + _block->JUMP(forcond); + + _block = forcond; + if (ast->condition) + condition(ast->condition, forbody, forend); + else + _block->JUMP(forbody); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(IfStatement *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock()) : 0; + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + + _block = iftrue; + statement(ast->ok); + _block->JUMP(endif); + + if (ast->ko) { + _block = iffalse; + statement(ast->ko); + _block->JUMP(endif); + } + + _block = endif; + + return false; +} + +bool Codegen::visit(LabelledStatement *ast) +{ + // check that no outer loop contains the label + Loop *l = _loop; + while (l) { + if (l->labelledStatement->label == ast->label) { + QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); + throwSyntaxError(ast->firstSourceLocation(), error); + } + l = l->parent; + } + _labelledStatement = ast; + + if (AST::cast<AST::SwitchStatement *>(ast->statement) || + AST::cast<AST::WhileStatement *>(ast->statement) || + AST::cast<AST::DoWhileStatement *>(ast->statement) || + AST::cast<AST::ForStatement *>(ast->statement) || + AST::cast<AST::ForEachStatement *>(ast->statement) || + AST::cast<AST::LocalForStatement *>(ast->statement) || + AST::cast<AST::LocalForEachStatement *>(ast->statement)) { + statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. + } else { + V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock()); + enterLoop(ast->statement, 0, breakBlock, /*continueBlock*/ 0); + statement(ast->statement); + _block->JUMP(breakBlock); + _block = breakBlock; + leaveLoop(); + } + + return false; +} + +bool Codegen::visit(LocalForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, foreachin, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(LocalForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, forcond, forend, forstep); + + variableDeclarationList(ast->declarations); + _block->JUMP(forcond); + + _block = forcond; + if (ast->condition) + condition(ast->condition, forbody, forend); + else + _block->JUMP(forbody); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(ReturnStatement *ast) +{ + if (_mode != FunctionCode && _mode != QmlBinding) + throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); + if (ast->expression) { + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + } + unwindException(0); + + _block->JUMP(_exitBlock); + return false; +} + +bool Codegen::visit(SwitchStatement *ast) +{ + V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock()); + + if (ast->block) { + Result lhs = expression(ast->expression); + V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock()); + _block->JUMP(switchcond); + V4IR::BasicBlock *previousBlock = 0; + + QHash<Node *, V4IR::BasicBlock *> blockMap; + + enterLoop(ast, 0, switchend, 0); + + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(groupStartBlock()); + 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(groupStartBlock()); + 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; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[clause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + leaveLoop(); + + _block->JUMP(switchend); + + _block = switchcond; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + if (ast->block->defaultClause) { + _block->JUMP(blockMap[ast->block->defaultClause]); + } + } + + _block->JUMP(switchend); + + _block = switchend; + return false; +} + +bool Codegen::visit(ThrowStatement *ast) +{ + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + _block->JUMP(_throwBlock); + return false; +} + +bool Codegen::visit(TryStatement *ast) +{ + _function->hasTry = true; + + if (_function->isStrict && ast->catchExpression && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + + V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock()); + // We always need a finally body to clean up the exception handler + V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock()); + + V4IR::BasicBlock *throwBlock = _function->newBasicBlock(groupStartBlock()); + V4IR::ExprList *throwArgs = _function->New<V4IR::ExprList>(); + throwArgs->expr = throwBlock->TEMP(_returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(catchBody); + qSwap(_throwBlock, throwBlock); + + int hasException = _block->newTemp(); + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + + // Pass the hidden "needRethrow" TEMP to the + // builtin_delete_exception_handler, in order to have those TEMPs alive for + // the duration of the exception handling block. + V4IR::ExprList *finishTryArgs = _function->New<V4IR::ExprList>(); + finishTryArgs->init(_block->TEMP(hasException)); + + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); + _scopeAndFinally = &tcf; + + int exception_to_rethrow = _block->newTemp(); + + _block->TRY(tryBody, catchBody, + ast->catchExpression ? ast->catchExpression->name.toString() : QString(), + _block->TEMP(exception_to_rethrow)); + + _block = tryBody; + statement(ast->statement); + _block->JUMP(finallyBody); + + _block = catchBody; + + if (ast->catchExpression) { + // check if an exception got thrown within catch. Go to finally + // and then rethrow + V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock()); + _block->CJUMP(_block->TEMP(hasException), finallyBody, b); + _block = b; + } + + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, true)); + + if (ast->catchExpression) { + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); + _scopeAndFinally = &scope; + statement(ast->catchExpression->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + } + _block->JUMP(finallyBody); + + _scopeAndFinally = tcf.parent; + + qSwap(_throwBlock, throwBlock); + + V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock()); + _block = finallyBody; + + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); + + if (ast->finallyExpression && ast->finallyExpression->statement) + statement(ast->finallyExpression->statement); + + V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock()); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); + _block->JUMP(_throwBlock); + + _block = after; + + 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: + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); + // fall through + case ScopeAndFinally::CatchScope: + _scopeAndFinally = _scopeAndFinally->parent; + --_function->insideWithOrCatch; + break; + case ScopeAndFinally::TryScope: { + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); + 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) +{ + variableDeclarationList(ast->declarations); + return false; +} + +bool Codegen::visit(WhileStatement *ast) +{ + V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond); + V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, whilecond, whileend, whilecond); + + _block->JUMP(whilecond); + _block = whilecond; + condition(ast->expression, whilebody, whileend); + + _block = whilebody; + statement(ast->statement); + _block->JUMP(whilecond); + + _block = whileend; + leaveLoop(); + + return false; +} + +bool Codegen::visit(WithStatement *ast) +{ + _function->hasWith = true; + + V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock()); + + _block->JUMP(withBlock); + _block = withBlock; + int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(withObject)); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_with_scope, 0, 0), args)); + + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + + V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock()); + _block->JUMP(next); + _block = next; + + return false; +} + +bool Codegen::visit(UiArrayBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectDefinition *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiPublicMember *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiScriptBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiSourceElement *) +{ + assert(!"not implemented"); + return false; +} + +void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr *expr, const SourceLocation& loc) +{ + if (!_env->isStrict) + return; + V4IR::Name *n = expr->asName(); + if (!n) + return; + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); +} + +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +{ + QV4::DiagnosticMessage *msg = new QV4::DiagnosticMessage; + msg->fileName = _fileName; + msg->offset = loc.begin(); + msg->startLine = loc.startLine; + msg->startColumn = loc.startColumn; + msg->message = detail; + if (_context) + _context->throwSyntaxError(msg); + else if (_errorHandler) + _errorHandler->syntaxError(msg); + else + Q_ASSERT(!"No error handler available."); +} + +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +{ + if (_context) + _context->throwReferenceError(QV4::Value::fromString(_context, detail), _fileName, loc.startLine); + else if (_errorHandler) + throwSyntaxError(loc, detail); + else + Q_ASSERT(!"No error handler available."); +} diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h new file mode 100644 index 0000000000..75ca915a13 --- /dev/null +++ b/src/qml/compiler/qv4codegen_p.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4CODEGEN_P_H +#define QV4CODEGEN_P_H + +#include "private/qv4global_p.h" +#include "qv4jsir_p.h" +#include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> +#include <QtCore/QStringList> +#include <assert.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct DiagnosticMessage; +struct ExecutionContext; +} + +namespace QQmlJS { +namespace AST { +class UiParameterList; +} + + + +class ErrorHandler +{ +public: + virtual void syntaxError(QV4::DiagnosticMessage *message) = 0; +}; + +class Q_QML_EXPORT Codegen: protected AST::Visitor +{ +public: + Codegen(QV4::ExecutionContext *ctx, bool strict); + Codegen(ErrorHandler *errorHandler, bool strictMode); + + enum Mode { + GlobalCode, + EvalCode, + FunctionCode, + QmlBinding + }; + + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::Program *ast, + V4IR::Module *module, + Mode mode = GlobalCode, + const QStringList &inheritedLocals = QStringList()); + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module); + +protected: + enum Format { ex, cx, nx }; + struct Result { + V4IR::Expr *code; + V4IR::BasicBlock *iftrue; + V4IR::BasicBlock *iffalse; + Format format; + Format requested; + + explicit Result(Format requested = ex) + : code(0) + , iftrue(0) + , iffalse(0) + , format(ex) + , requested(requested) {} + + explicit Result(V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) + : code(0) + , iftrue(iftrue) + , iffalse(iffalse) + , format(ex) + , requested(cx) {} + + inline V4IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } + inline V4IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + + bool accept(Format f) + { + if (requested == f) { + format = f; + return true; + } + return false; + } + }; + + struct Environment { + Environment *parent; + + enum MemberType { + UndefinedMember, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + struct Member { + MemberType type; + int index; + AST::FunctionExpression *function; + }; + typedef QMap<QString, Member> MemberMap; + + MemberMap members; + AST::FormalParameterList *formals; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; + bool isStrict; + bool isNamedFunctionExpression; + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject; + + Environment(Environment *parent) + : parent(parent) + , formals(0) + , maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) + , isStrict(false) + , isNamedFunctionExpression(false) + , usesArgumentsObject(ArgumentsObjectUnknown) + { + if (parent && parent->isStrict) + isStrict = true; + } + + int findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return -1; + assert((*it).index != -1 || !parent); + return (*it).index; + } + + 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; + } + + void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + { + 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; + members.insert(name, m); + } else { + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } + } + } + } + }; + + Environment *newEnvironment(AST::Node *node, Environment *parent) + { + Environment *env = new Environment(parent); + _envMap.insert(node, env); + return env; + } + + struct UiMember { + }; + + struct ScopeAndFinally { + enum ScopeType { + WithScope, + TryScope, + CatchScope + }; + + ScopeAndFinally *parent; + AST::Finally *finally; + V4IR::ExprList *finishTryArgs; + ScopeType type; + + ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), finishTryArgs(0), type(t) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, V4IR::ExprList *finishTryArgs) + : parent(parent), finally(finally), finishTryArgs(finishTryArgs), type(TryScope) + {} + }; + + struct Loop { + AST::LabelledStatement *labelledStatement; + AST::Statement *node; + V4IR::BasicBlock *groupStartBlock; + V4IR::BasicBlock *breakBlock; + V4IR::BasicBlock *continueBlock; + Loop *parent; + ScopeAndFinally *scopeAndFinally; + + Loop(AST::Statement *node, V4IR::BasicBlock *groupStartBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent) + : labelledStatement(0), node(node), groupStartBlock(groupStartBlock), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} + }; + + void enterEnvironment(AST::Node *node); + void leaveEnvironment(); + + void enterLoop(AST::Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock); + void leaveLoop(); + V4IR::BasicBlock *groupStartBlock() const + { + for (Loop *it = _loop; it; it = it->parent) + if (it->groupStartBlock) + return it->groupStartBlock; + return 0; + } + + V4IR::Expr *member(V4IR::Expr *base, const QString *name); + V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); + V4IR::Expr *argument(V4IR::Expr *expr); + V4IR::Expr *reference(V4IR::Expr *expr); + V4IR::Expr *unop(V4IR::AluOp op, V4IR::Expr *expr); + V4IR::Expr *binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right); + V4IR::Expr *call(V4IR::Expr *base, V4IR::ExprList *args); + void move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op = V4IR::OpInvalid); + void cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + + V4IR::Function *defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, + Mode mode = FunctionCode, + const QStringList &inheritedLocals = QStringList()); + + void unwindException(ScopeAndFinally *outest); + + void statement(AST::Statement *ast); + void statement(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + Result expression(AST::ExpressionNode *ast); + QString propertyName(AST::PropertyName *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 variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclarationList(AST::VariableDeclarationList *ast); + + V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + + // nodes + virtual bool visit(AST::ArgumentList *ast); + virtual bool visit(AST::CaseBlock *ast); + virtual bool visit(AST::CaseClause *ast); + virtual bool visit(AST::CaseClauses *ast); + virtual bool visit(AST::Catch *ast); + virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::ElementList *ast); + virtual bool visit(AST::Elision *ast); + virtual bool visit(AST::Finally *ast); + virtual bool visit(AST::FormalParameterList *ast); + virtual bool visit(AST::FunctionBody *ast); + virtual bool visit(AST::Program *ast); + virtual bool visit(AST::PropertyNameAndValue *ast); + virtual bool visit(AST::PropertyAssignmentList *ast); + virtual bool visit(AST::PropertyGetterSetter *ast); + virtual bool visit(AST::SourceElements *ast); + virtual bool visit(AST::StatementList *ast); + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *ast); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::VariableDeclaration *ast); + virtual bool visit(AST::VariableDeclarationList *ast); + + // expressions + virtual bool visit(AST::Expression *ast); + virtual bool visit(AST::ArrayLiteral *ast); + virtual bool visit(AST::ArrayMemberExpression *ast); + virtual bool visit(AST::BinaryExpression *ast); + virtual bool visit(AST::CallExpression *ast); + virtual bool visit(AST::ConditionalExpression *ast); + virtual bool visit(AST::DeleteExpression *ast); + virtual bool visit(AST::FalseLiteral *ast); + virtual bool visit(AST::FieldMemberExpression *ast); + virtual bool visit(AST::FunctionExpression *ast); + virtual bool visit(AST::IdentifierExpression *ast); + virtual bool visit(AST::NestedExpression *ast); + virtual bool visit(AST::NewExpression *ast); + virtual bool visit(AST::NewMemberExpression *ast); + virtual bool visit(AST::NotExpression *ast); + virtual bool visit(AST::NullExpression *ast); + virtual bool visit(AST::NumericLiteral *ast); + virtual bool visit(AST::ObjectLiteral *ast); + virtual bool visit(AST::PostDecrementExpression *ast); + virtual bool visit(AST::PostIncrementExpression *ast); + virtual bool visit(AST::PreDecrementExpression *ast); + virtual bool visit(AST::PreIncrementExpression *ast); + virtual bool visit(AST::RegExpLiteral *ast); + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::ThisExpression *ast); + virtual bool visit(AST::TildeExpression *ast); + virtual bool visit(AST::TrueLiteral *ast); + virtual bool visit(AST::TypeOfExpression *ast); + virtual bool visit(AST::UnaryMinusExpression *ast); + virtual bool visit(AST::UnaryPlusExpression *ast); + virtual bool visit(AST::VoidExpression *ast); + virtual bool visit(AST::FunctionDeclaration *ast); + + // property names + virtual bool visit(AST::IdentifierPropertyName *ast); + virtual bool visit(AST::NumericLiteralPropertyName *ast); + virtual bool visit(AST::StringLiteralPropertyName *ast); + + // source elements + virtual bool visit(AST::FunctionSourceElement *ast); + virtual bool visit(AST::StatementSourceElement *ast); + + // statements + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::BreakStatement *ast); + virtual bool visit(AST::ContinueStatement *ast); + virtual bool visit(AST::DebuggerStatement *ast); + virtual bool visit(AST::DoWhileStatement *ast); + virtual bool visit(AST::EmptyStatement *ast); + virtual bool visit(AST::ExpressionStatement *ast); + virtual bool visit(AST::ForEachStatement *ast); + virtual bool visit(AST::ForStatement *ast); + virtual bool visit(AST::IfStatement *ast); + virtual bool visit(AST::LabelledStatement *ast); + virtual bool visit(AST::LocalForEachStatement *ast); + virtual bool visit(AST::LocalForStatement *ast); + virtual bool visit(AST::ReturnStatement *ast); + virtual bool visit(AST::SwitchStatement *ast); + virtual bool visit(AST::ThrowStatement *ast); + virtual bool visit(AST::TryStatement *ast); + virtual bool visit(AST::VariableStatement *ast); + virtual bool visit(AST::WhileStatement *ast); + virtual bool visit(AST::WithStatement *ast); + + // ui object members + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + + void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr* expr, const AST::SourceLocation &loc); + + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + +private: + QString _fileName; + Result _expr; + QString _property; + UiMember _uiMember; + V4IR::Module *_module; + V4IR::Function *_function; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_exitBlock; + V4IR::BasicBlock *_throwBlock; + unsigned _returnAddress; + Mode _mode; + Environment *_env; + Loop *_loop; + AST::LabelledStatement *_labelledStatement; + ScopeAndFinally *_scopeAndFinally; + QHash<AST::Node *, Environment *> _envMap; + QHash<AST::FunctionExpression *, int> _functionMap; + QV4::ExecutionContext *_context; + bool _strictMode; + ErrorHandler *_errorHandler; + + class ScanFunctions; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp new file mode 100644 index 0000000000..ec68ede72d --- /dev/null +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h new file mode 100644 index 0000000000..ac7196e2d1 --- /dev/null +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -0,0 +1,621 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include <QtCore/qglobal.h> +#include <private/qv4object_p.h> + +QT_BEGIN_NAMESPACE + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Ret, ret) \ + F(LoadValue, loadValue) \ + F(LoadClosure, loadClosure) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ + F(Push, push) \ + F(EnterTry, enterTry) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ + F(CallElement, callElement) \ + F(CallActivationProperty, callActivationProperty) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinFinishTry, callBuiltinFinishTry) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArray, callBuiltinDefineArray) \ + F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ + F(Jump, jump) \ + F(CJump, cjump) \ + F(Unop, unop) \ + F(Binop, binop) \ + F(BinopContext, binopContext) \ + F(AddNumberParams, addNumberParams) \ + F(MulNumberParams, mulNumberParams) \ + F(SubNumberParams, subNumberParams) \ + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; \ + unsigned int breakPoint : 1; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; \ + unsigned int breakPoint : 1; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3, + ScopedLocalType = 4 + }; + QV4::Value value; + unsigned type : 3; + unsigned scope : 29; + unsigned index; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + bool isScopedLocal() const { return type == ScopedLocalType; } + + static Param createValue(const QV4::Value &v) + { + Param p; + p.type = ValueType; + p.scope = 0; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx, uint scope) + { + Param p; + p.type = ArgumentType; + p.scope = scope; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createScopedLocal(unsigned idx, uint scope) + { + Param p; + p.type = ScopedLocalType; + p.scope = scope; + p.index = idx; + return p; + } + + inline bool operator==(const Param &other) const + { return type == other.type && scope == other.scope && index == other.index; } + + inline bool operator!=(const Param &other) const + { return !(*this == other); } + }; + + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_common { + MOTH_INSTR_HEADER + }; + struct instr_ret { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + Param source; + Param result; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + QV4::Function *value; + Param result; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + QV4::String *name; + Param source; + }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param base; + Param result; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param base; + Param source; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param source; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_enterTry { + MOTH_INSTR_HEADER + ptrdiff_t tryOffset; + ptrdiff_t catchOffset; + QV4::String *exceptionVarName; + Param exceptionVar; + }; + struct instr_callValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param dest; + Param result; + }; + struct instr_callProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_callElement { + MOTH_INSTR_HEADER + Param base; + Param index; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinThrow { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinFinishTry { + MOTH_INSTR_HEADER + }; + 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 + QV4::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + QV4::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + Param base; + QV4::String *member; + Param result; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + Param base; + QV4::String *member; + Param result; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + QV4::String *varName; + bool isDeletable; + }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + QV4::String *name; + Param object; + Param getter; + Param setter; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param object; + Param value; + }; + struct instr_callBuiltinDefineArray { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinDefineObjectLiteral { + MOTH_INSTR_HEADER + QV4::InternalClass *internalClass; + quint32 args; + Param result; + }; + struct instr_createValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param func; + Param result; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_unop { + MOTH_INSTR_HEADER + QV4::UnaryOpName alu; + Param source; + Param result; + }; + struct instr_binop { + MOTH_INSTR_HEADER + QV4::BinOp alu; + Param lhs; + Param rhs; + Param result; + }; + struct instr_binopContext { + MOTH_INSTR_HEADER + QV4::BinOpContext alu; + Param lhs; + Param rhs; + Param result; + }; + struct instr_addNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_mulNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_subNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_loadThis { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpElement alu; + Param base; + Param index; + Param source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpMember alu; + QV4::String *member; + Param base; + Param source; + }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpName alu; + QV4::String *name; + Param source; + }; + + instr_common common; + instr_ret ret; + instr_loadValue loadValue; + instr_moveTemp moveTemp; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; + instr_push push; + instr_enterTry enterTry; + instr_callValue callValue; + instr_callProperty callProperty; + instr_callElement callElement; + instr_callActivationProperty callActivationProperty; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinFinishTry callBuiltinFinishTry; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArray callBuiltinDefineArray; + instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; + instr_jump jump; + instr_cjump cjump; + instr_unop unop; + instr_binop binop; + instr_binopContext binopContext; + instr_addNumberParams addNumberParams; + instr_mulNumberParams mulNumberParams; + instr_subNumberParams subNumberParams; + instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; + + static int size(Type type); +}; + +template<int N> +struct InstrMeta { +}; + +#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; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template<int InstrType> +class InstrData : public InstrMeta<InstrType>::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4INSTR_MOTH_P_H diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp new file mode 100644 index 0000000000..4317ba0d54 --- /dev/null +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -0,0 +1,1466 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_masm_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include "qv4regexpobject_p.h" +#include "qv4unwindhelper_p.h" +#include "qv4lookup_p.h" +#include "qv4function_p.h" +#include "qv4ssa_p.h" +#include "qv4exception_p.h" + +#include <assembler/LinkBuffer.h> +#include <WTFStubs.h> + +#include <iostream> +#include <cassert> + +#if USE(UDIS86) +# include <udis86.h> +#endif + +using namespace QQmlJS; +using namespace QQmlJS::MASM; +using namespace QV4; + +namespace { +class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor +{ + int _nextFreeStackSlot; + QHash<V4IR::Temp, int> _stackSlotForTemp; + + void renumber(V4IR::Temp *t) + { + if (t->kind != V4IR::Temp::VirtualRegister) + return; + + int stackSlot = _stackSlotForTemp.value(*t, -1); + if (stackSlot == -1) { + stackSlot = _nextFreeStackSlot++; + _stackSlotForTemp[*t] = stackSlot; + } + + t->kind = V4IR::Temp::StackSlot; + t->index = stackSlot; + } + +public: + ConvertTemps() + : _nextFreeStackSlot(0) + {} + + void toStackSlots(V4IR::Function *function) + { + _stackSlotForTemp.reserve(function->tempCount); + + foreach (V4IR::BasicBlock *bb, function->basicBlocks) + foreach (V4IR::Stmt *s, bb->statements) + s->accept(this); + + function->tempCount = _nextFreeStackSlot; + } + +protected: + virtual void visitConst(V4IR::Const *) {} + virtual void visitString(V4IR::String *) {} + virtual void visitRegExp(V4IR::RegExp *) {} + virtual void visitName(V4IR::Name *) {} + virtual void visitTemp(V4IR::Temp *e) { renumber(e); } + virtual void visitClosure(V4IR::Closure *) {} + virtual void visitConvert(V4IR::Convert *e) { e->expr->accept(this); } + virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(V4IR::Call *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(V4IR::New *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } + virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } + virtual void visitMove(V4IR::Move *s) { s->target->accept(this); s->source->accept(this); } + virtual void visitJump(V4IR::Jump *) {} + virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } + virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); } + virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); } +}; +} // anonymous namespace + +/* Platform/Calling convention/Architecture specific section */ + +#if CPU(X86_64) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::rbx, + // Not used: JSC::X86Registers::r10, + JSC::X86Registers::r12, // LocalsRegister + // Not used: JSC::X86Registers::r13, + JSC::X86Registers::r14 // ContextRegister + // Not used: JSC::X86Registers::r15, +}; +#endif + +#if CPU(X86) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::ebx, + JSC::X86Registers::esi, // ContextRegister + JSC::X86Registers::edi // LocalsRegister +}; +#endif + +#if CPU(ARM) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // ### FIXME: remove unused registers. + // Keep these in reverse order and make sure to also edit the unwind program in + // qv4unwindhelper_p-arm.h when changing this list. + JSC::ARMRegisters::r12, + JSC::ARMRegisters::r10, + JSC::ARMRegisters::r9, + JSC::ARMRegisters::r8, + JSC::ARMRegisters::r7, + JSC::ARMRegisters::r6, + JSC::ARMRegisters::r5, + JSC::ARMRegisters::r4 +}; +#endif + +const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); + +/* End of platform/calling convention/architecture specific section */ + + +const Assembler::VoidType Assembler::Void; + +Assembler::Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine) + : _function(function), _vmFunction(vmFunction), _engine(engine), _nextBlock(0) +{ +} + +void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock) +{ + _addrs[block] = label(); + _nextBlock = nextBlock; +} + +void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target) +{ + if (target != _nextBlock) + _patches[target].append(jump()); +} + +void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump) +{ + _patches[targetBlock].append(targetJump); +} + +void Assembler::addPatch(DataLabelPtr patch, Label target) +{ + DataLabelPatch p; + p.dataLabel = patch; + p.target = target; + _dataLabelPatches.append(p); +} + +void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target) +{ + _labelPatches[target].append(patch); +} + +Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, V4IR::Temp *t) +{ + int32_t offset = 0; + int scope = t->scope; + QV4::Function *f = _vmFunction; + RegisterID context = ContextRegister; + if (scope) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister); + --scope; + f = f->outer; + context = ScratchRegister; + while (scope) { + loadPtr(Address(context, offsetof(ExecutionContext, outer)), context); + f = f->outer; + --scope; + } + } + switch (t->kind) { + case V4IR::Temp::Formal: + case V4IR::Temp::ScopedFormal: { + loadPtr(Address(context, offsetof(CallContext, arguments)), reg); + offset = t->index * sizeof(Value); + } break; + case V4IR::Temp::Local: + case V4IR::Temp::ScopedLocal: { + loadPtr(Address(context, offsetof(CallContext, locals)), reg); + offset = t->index * sizeof(Value); + } break; + case V4IR::Temp::StackSlot: { + assert(t->scope == 0); + const int arg = _function->maxNumberOfArguments + t->index + 1; + offset = - sizeof(Value) * (arg + 1); + offset -= sizeof(void*) * calleeSavedRegisterCount; + reg = LocalsRegister; + } break; + default: + Q_UNIMPLEMENTED(); + } + return Pointer(reg, offset); +} + +template <typename Result, typename Source> +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister); + storeReturnValue(result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +template <typename Result> +void Assembler::copyValue(Result result, V4IR::Expr* source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister); + storeReturnValue(result); +#else + if (V4IR::Temp *temp = source->asTemp()) { + loadDouble(temp, FPGpr0); + storeDouble(FPGpr0, result); + } else if (V4IR::Const *c = source->asConst()) { + QV4::Value v = convertToValue(c); + storeValue(v, result); + } else { + assert(! "not implemented"); + } +#endif +} + + +void Assembler::storeValue(QV4::Value value, V4IR::Temp* destination) +{ + Address addr = loadTempAddress(ScratchRegister, destination); + storeValue(value, addr); +} + +int Assembler::calculateStackFrameSize(int locals) +{ + const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry + + RegisterSize; // saved StackFrameRegister + + // space for the locals and the callee saved registers + int frameSize = locals * sizeof(QV4::Value) + sizeof(void*) * calleeSavedRegisterCount; + + frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise); + frameSize -= stackSpaceAllocatedOtherwise; + + return frameSize; +} + +void Assembler::enterStandardStackFrame(int locals) +{ + platformEnterStandardStackFrame(); + + // ### FIXME: Handle through calleeSavedRegisters mechanism + // or eliminate StackFrameRegister altogether. + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + + int frameSize = calculateStackFrameSize(locals); + + subPtr(TrustedImm32(frameSize), StackPointerRegister); + + for (int i = 0; i < calleeSavedRegisterCount; ++i) + storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); + + move(StackFrameRegister, LocalsRegister); +} + +void Assembler::leaveStandardStackFrame(int locals) +{ + // restore the callee saved registers + for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) + loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); + + int frameSize = calculateStackFrameSize(locals); + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. +#if CPU(ARM_THUMB2) + move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); + add32(JSC::ARMRegisters::r3, StackPointerRegister); +#else + addPtr(TrustedImm32(frameSize), StackPointerRegister); +#endif + + pop(StackFrameRegister); + platformLeaveStandardStackFrame(); +} + + + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0, 0 } +#define OPCONTEXT(op) \ + { isel_stringIfy(op), 0, op, 0, 0 } + +#define INLINE_OP(op, memOp, immOp) \ + { isel_stringIfy(op), op, 0, memOp, immOp } +#define INLINE_OPCONTEXT(op, memOp, immOp) \ + { isel_stringIfy(op), 0, op, memOp, immOp } + +#define NULL_OP \ + { 0, 0, 0, 0, 0 } + +const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::LastAluOp + 1] = { + NULL_OP, // OpInvalid + NULL_OP, // OpIfTrue + NULL_OP, // OpNot + NULL_OP, // OpUMinus + NULL_OP, // OpUPlus + NULL_OP, // OpCompl + NULL_OP, // OpIncrement + NULL_OP, // OpDecrement + + INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor + + INLINE_OPCONTEXT(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul + + OP(__qmljs_div), // OpDiv + OP(__qmljs_mod), // OpMod + + INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift + INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift + + OP(__qmljs_gt), // OpGt + OP(__qmljs_lt), // OpLt + OP(__qmljs_ge), // OpGe + OP(__qmljs_le), // OpLe + OP(__qmljs_eq), // OpEqual + OP(__qmljs_ne), // OpNotEqual + OP(__qmljs_se), // OpStrictEqual + OP(__qmljs_sne), // OpStrictNotEqual + + OPCONTEXT(__qmljs_instanceof), // OpInstanceof + OPCONTEXT(__qmljs_in), // OpIn + + NULL_OP, // OpAnd + NULL_OP // OpOr +}; + +void Assembler::generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp *left, V4IR::Temp *right) +{ + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation && !info.contextImplementation) { + assert(!"unreachable"); + return; + } + + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); + + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; + + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } + } + + Jump binOpFinished; + + if (canDoInline) { + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(QV4::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(QV4::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(QV4::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(QV4::Value, int_32); + + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); + } else { // right->asConst() + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(QV4::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(QV4::Value, tag); + store32(TrustedImm32(QV4::Value::_Integer_Type), resultTypeAddr); + + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); + } + + // Fallback + if (info.contextImplementation) + generateFunctionCallImp(Assembler::Void, info.name, info.contextImplementation, ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); + else + generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); + + if (binOpFinished.isSet()) + binOpFinished.link(this); +} +#if OS(LINUX) || OS(MAC_OS_X) +static void printDisassembledOutputWithCalls(const char* output, const QHash<void*, const char*>& functions, + const QVector<String*> &identifiers) +{ + QByteArray processedOutput(output); + for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); + ptrString.prepend("0x"); + processedOutput = processedOutput.replace(ptrString, it.value()); + } + for (QVector<String*>::ConstIterator it = identifiers.begin(), end = identifiers.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(*it), 16); + ptrString.prepend("0x"); + QByteArray replacement = "\"" + (*it)->toQString().toUtf8() + "\""; + processedOutput = processedOutput.replace(ptrString, replacement); + } + fprintf(stderr, "%s\n", processedOutput.constData()); +} +#endif + +void Assembler::recordLineNumber(int lineNumber) +{ + CodeLineNumerMapping mapping; + mapping.location = label(); + mapping.lineNumber = lineNumber; + codeLineNumberMappings << mapping; +} + + +void Assembler::link(QV4::Function *vmFunc) +{ + Label endOfCode = label(); +#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) + // Let the ARM exception table follow right after that + for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i) + nop(); +#endif + + { + QHashIterator<V4IR::BasicBlock *, QVector<Jump> > it(_patches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (Jump jump, it.value()) + jump.linkTo(target, this); + } + } + + JSC::JSGlobalData dummy(_engine->executableAllocator); + JSC::LinkBuffer linkBuffer(dummy, this, 0); + vmFunc->codeSize = linkBuffer.offsetOf(endOfCode); + + vmFunc->lineNumberMappings.resize(codeLineNumberMappings.count()); + for (int i = 0; i < codeLineNumberMappings.count(); ++i) { + QV4::LineNumberMapping mapping; + mapping.codeOffset = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); + mapping.lineNumber = codeLineNumberMappings.at(i).lineNumber; + vmFunc->lineNumberMappings[i] = mapping; + } + + QHash<void*, const char*> functions; + foreach (CallToLink ctl, _callsToLink) { + linkBuffer.link(ctl.call, ctl.externalFunction); + functions[ctl.externalFunction.value()] = ctl.functionName; + } + + foreach (const DataLabelPatch &p, _dataLabelPatches) + linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + + { + QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (DataLabelPtr label, it.value()) + linkBuffer.patch(label, linkBuffer.locationOf(target)); + } + } + +#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) + UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode)); +#endif + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { +#if OS(LINUX) && !defined(Q_OS_ANDROID) + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); +#elif OS(MAC_OS_X) + struct MemStream { + QByteArray buf; + static int write(void *cookie, const char *buf, int len) { + MemStream *stream = reinterpret_cast<MemStream *>(cookie); + stream->buf.append(buf, len); + return len; + } + }; + MemStream memStream; + + FILE* disasmStream = fwopen(&memStream, MemStream::write); + WTF::setDataFile(disasmStream); +#endif + + QByteArray name = _function->name->toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(quintptr(_function), 16); + name.prepend("IR::Function(0x"); + name.append(")"); + } + vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + + WTF::setDataFile(stderr); +#if (OS(LINUX) && !defined(Q_OS_ANDROID)) || OS(MAC_OS_X) + fclose(disasmStream); +# if OS(MAC_OS_X) + char *disasmOutput = memStream.buf.data(); +# endif +# if CPU(X86) || CPU(X86_64) + QHash<void*, String*> idents; + printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers); +# endif +# if OS(LINUX) + free(disasmOutput); +# endif +#endif + } else { + vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + vmFunc->code = (Value (*)(QV4::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); +} + +InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _block(0) + , _function(0) + , _vmFunction(0) + , _as(0) + , _locals(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ + delete _as; +} + +void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) +{ + QVector<Lookup> lookups; + QSet<V4IR::BasicBlock*> reentryBlocks; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + Assembler* oldAssembler = _as; + _as = new Assembler(_function, _vmFunction, engine()); + + V4IR::Optimizer opt(_function); + opt.run(); + if (opt.isInSSA()) { +#if CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX)) && 0 + // TODO: add a register allocator here. +#else + // No register allocator available for this platform, so: + opt.convertOutOfSSA(); + ConvertTemps().toStackSlots(_function); +#endif + } else { + ConvertTemps().toStackSlots(_function); + } + + int locals = (_function->tempCount + _function->maxNumberOfArguments) + 1; + locals = (locals + 1) & ~1; + qSwap(_locals, locals); + _as->enterStandardStackFrame(_locals); + + int contextPointer = 0; +#if !defined(RETURN_VALUE_IN_REGISTER) + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif + +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); +#else + _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); +#endif + + for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; + _block = _function->basicBlocks[i]; + _as->registerBlock(_block, nextBlock); + + if (_reentryBlocks.contains(_block)) { + _as->enterStandardStackFrame(/*locals*/0); +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); + _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); +#else + _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); + _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); +#endif + } + + foreach (V4IR::Stmt *s, _block->statements) { + if (s->location.isValid()) + _as->recordLineNumber(s->location.startLine); + s->accept(this); + } + } + + _as->link(_vmFunction); + + if (_lookups.size()) { + _vmFunction->lookups = new Lookup[_lookups.size()]; + memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); + } + + UnwindHelper::registerFunction(_vmFunction); + + qSwap(_vmFunction, vmFunction); + qSwap(_function, function); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + qSwap(_locals, locals); + delete _as; + _as = oldAssembler; +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(*func->id); + + if (useFastLookups && func->global) { + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_activation_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value)); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + _as->storeValue(Value::fromBoolean(false), result); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index)); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister, Assembler::Reference(arg)); +} + +typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr)); +static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody, + QV4::String *exceptionVarName, Value *exceptionVar) +{ + *exceptionVar = Value::undefinedValue(); + void *addressToContinueAt = 0; + try { + addressToContinueAt = tryBody(context, localsPtr); + } catch (Exception& ex) { + ex.accept(context); + *exceptionVar = ex.value(); + try { + ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, ex.value(), context); + addressToContinueAt = catchBody(catchContext, localsPtr); + context = __qmljs_builtin_pop_scope(catchContext); + } catch (Exception& ex) { + *exceptionVar = ex.value(); + ex.accept(context); + addressToContinueAt = catchBody(context, localsPtr); + } + } + return addressToContinueAt; +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end + // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper + // returns and to which we jump to. + + _reentryBlocks.insert(t->tryBlock); + _reentryBlocks.insert(t->catchBlock); + + generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, + Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock), + identifier(t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar)); + _as->jump(Assembler::ReturnValueRegister); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper + // with the address that we'd like to continue at, which is right after the ret below. + Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); + _as->leaveStandardStackFrame(/*locals*/0); + _as->ret(); + _as->addPatch(continuation, _as->label()); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPopScope() +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable), identifier(name)); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter)); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + int length = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister, + Assembler::PointerToValue(result), + baseAddressForCallArguments(), Assembler::TrustedImm32(length)); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + int argc = 0; + + InternalClass *klass = engine()->emptyClass; + V4IR::ExprList *it = args; + while (it) { + V4IR::Name *name = it->expr->asName(); + it = it->next; + + bool isData = it->expr->asConst()->value; + it = it->next; + klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); + + _as->copyValue(argumentAddressForCall(argc++), it->expr); + + if (!isData) { + it = it->next; + _as->copyValue(argumentAddressForCall(argc++), it->expr); + } + + it = it->next; + } + + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister, + Assembler::PointerToValue(result), baseAddressForCallArguments(), + Assembler::TrustedImmPtr(klass)); +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + V4IR::Temp* thisObject = 0; + generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), + Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ +#if defined(VALUE_FITS_IN_REGISTER) + _as->load64(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister); + _as->storeReturnValue(temp); +#else + _as->copyValue(temp, Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject))); +#endif +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + _as->storeValue(convertToValue(sourceConst), targetTemp); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Value v = Value::fromString(identifier(str)); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + String *propertyName = identifier(*name->id); + if (useFastLookups && name->global) { + uint index = addGlobalLookup(propertyName); + generateLookupCall(index, offsetof(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::PointerToValue(temp)); + return; + } + generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + String *propertyName = identifier(targetName); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, + Assembler::ContextRegister, propertyName, Assembler::Reference(source)); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + QV4::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc)); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + if (useFastLookups) { + QV4::String *s = identifier(name); + uint index = addLookup(s); + generateLookupCall(index, offsetof(QV4::Lookup, getter), Assembler::PointerToValue(target), + Assembler::Reference(base)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::Reference(base), identifier(name)); + } +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + if (useFastLookups) { + QV4::String *s = identifier(targetName); + uint index = addSetterLookup(s); + generateLookupCall(index, offsetof(QV4::Lookup, setter), Assembler::Reference(targetBase), Assembler::Reference(source)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, + Assembler::Reference(targetBase), + identifier(targetName), Assembler::Reference(source)); + } +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, + Assembler::Reference(targetBase), Assembler::Reference(targetIndex), + Assembler::Reference(source)); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + _as->copyValue(targetTemp, sourceTemp); +} + +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) +#define setOpContext(op, opName, operation) \ + do { opContext = operation; opName = isel_stringIfy(operation); } while (0) + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + UnaryOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: setOp(op, opName, __qmljs_not); break; + case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; + default: assert(!"unreachable"); break; + } // switch + + if (op) + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), + Assembler::Reference(sourceTemp)); +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ + Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); + _as->generateBinOp(oper, target, leftSource->asTemp(), rightSource->asTemp()); +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + InplaceBinOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; + default: + Q_UNREACHABLE(); + break; + } + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + identifier(targetName), Assembler::Reference(rightSource)); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + InplaceBinOpElement op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp), + Assembler::Reference(source)); + } +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + InplaceBinOpMember op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + String* member = identifier(targetName); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBase), identifier(targetName), + Assembler::Reference(source)); + } +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, + V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(name); + + if (useFastLookups) { + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_call_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) +{ + // FIXME: do something more useful with this info + copyValue(source, target); +} + +String *InstructionSelection::identifier(const QString &s) +{ + String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(func != 0); + + if (useFastLookups && func->global) { + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(*func->id); + + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_construct_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + return; + } + + callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(value != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + _as->jumpToBlock(_block, s->target); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + if (V4IR::Temp *t = s->cond->asTemp()) { + Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); + Address tag = temp; + tag.offset += offsetof(QV4::Value, tag); + Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); + + Address data = temp; + data.offset += offsetof(QV4::Value, int_32); + _as->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _as->jump(); + + booleanConversion.link(_as); + { + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t)); + } + + testBoolean.link(_as); + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else if (V4IR::Binop *b = s->cond->asBinop()) { + if (b->left->asTemp() && b->right->asTemp()) { + CmpOp op = 0; + CmpOpContext opContext = 0; + const char *opName = 0; + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; + case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; + case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; + case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; + case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; + case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; + case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; + case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; + case V4IR::OpInstanceof: setOpContext(op, opName, __qmljs_cmp_instanceof); break; + case V4IR::OpIn: setOpContext(op, opName, __qmljs_cmp_in); break; + } // switch + + if (opContext) + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext, Assembler::ContextRegister, + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); + else + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); + + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else { + assert(!"wip"); + } + Q_UNIMPLEMENTED(); + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + if (V4IR::Temp *t = s->expr->asTemp()) { +#if defined(RETURN_VALUE_IN_REGISTER) +#if CPU(X86) + Address addr = _as->loadTempAddress(Assembler::ScratchRegister, t); + _as->load32(addr, JSC::X86Registers::eax); + addr.offset += 4; + _as->load32(addr, JSC::X86Registers::edx); +#else + _as->copyValue(Assembler::ReturnValueRegister, t); +#endif +#else + _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t); +#endif + } else if (V4IR::Const *c = s->expr->asConst()) { + _as->copyValue(Assembler::ReturnValueRegister, c); + } else { + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + Q_UNUSED(s); + } + + _as->leaveStandardStackFrame(_locals); +#if !defined(ARGUMENTS_IN_REGISTERS) && !defined(RETURN_VALUE_IN_REGISTER) + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + _as->pop(Assembler::ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + _as->poke(Assembler::ScratchRegister); +#endif + _as->ret(); +} + +int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args) +{ + int argc = 0; + for (V4IR::ExprList *it = args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (V4IR::ExprList *it = args; it; it = it->next, ++i) { +// V4IR::Temp *arg = it->expr->asTemp(); +// assert(arg != 0); + _as->copyValue(argumentAddressForCall(i), it->expr); + } + + return argc; +} + +void InstructionSelection::callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args) +{ + V4IR::Name *baseName = base->asName(); + assert(baseName != 0); + + int argc = prepareVariableArguments(args); + _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result), + identifier(*baseName->id), baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + + +uint InstructionSelection::addLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.getter = Lookup::getterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addSetterLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.setter = Lookup::setterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addGlobalLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.globalGetter = Lookup::globalGetterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h new file mode 100644 index 0000000000..29fc8a5d67 --- /dev/null +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -0,0 +1,939 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_MASM_P_H +#define QV4ISEL_MASM_P_H + +#include "private/qv4global_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "private/qv4object_p.h" +#include "private/qv4runtime_p.h" +#include "private/qv4lookup_p.h" + +#include <QtCore/QHash> +#include <config.h> +#include <wtf/Vector.h> +#include <assembler/MacroAssembler.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace MASM { + +class Assembler : public JSC::MacroAssembler +{ +public: + Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine); +#if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER +#undef ARGUMENTS_IN_REGISTERS + +#if OS(WINDOWS) + // Returned in EAX:EDX pair +#define RETURN_VALUE_IN_REGISTER +#else +#undef RETURN_VALUE_IN_REGISTER +#endif + +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 4; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#define RETURN_VALUE_IN_REGISTER +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID ContextRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 8; + +#if OS(WINDOWS) + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::ecx, + JSC::X86Registers::edx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 32; +#else // Unix + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 0; +#endif + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#undef RETURN_VALUE_IN_REGISTER +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND + + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const int RegisterSize = 4; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast<RegisterID>(JSC::ARMRegisters::r0 + index); + }; + + // Registers saved in platformEnterStandardStackFrame below. + static const int StackSpaceAllocatedUponFunctionEntry = 5 * RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() + { + // Move the register arguments onto the stack as if they were + // pushed by the caller, just like on ia32. This gives us consistent + // access to the parameters if we need to. + push(JSC::ARMRegisters::r3); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r0); + push(JSC::ARMRegisters::lr); + } + inline void platformLeaveStandardStackFrame() + { + pop(JSC::ARMRegisters::lr); + addPtr(TrustedImm32(4 * RegisterSize), StackPointerRegister); + } +#else +#error The JIT needs to be ported to this platform. +#endif + static const int calleeSavedRegisterCount; + +#if CPU(X86) || CPU(X86_64) + static const int StackAlignment = 16; +#elif CPU(ARM) + // Per AAPCS + static const int StackAlignment = 8; +#else +#error Stack alignment unknown for this platform. +#endif + + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + + struct VoidType { VoidType() {} }; + static const VoidType Void; + + + typedef JSC::FunctionPtr FunctionPtr; + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + struct PointerToValue { + PointerToValue(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + struct Reference { + Reference(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + + struct ReentryBlock { + ReentryBlock(V4IR::BasicBlock *b) : block(b) {} + V4IR::BasicBlock *block; + }; + + void callAbsolute(const char* functionName, FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + ctl.functionName = functionName; + _callsToLink.append(ctl); + } + + void callAbsolute(const char* /*functionName*/, Address addr) { + call(addr); + } + + void registerBlock(V4IR::BasicBlock*, V4IR::BasicBlock *nextBlock); + void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target); + void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); + void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target); + + Pointer loadTempAddress(RegisterID reg, V4IR::Temp *t); + + void loadArgumentInRegister(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgumentInRegister(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgumentInRegister(PointerToValue temp, RegisterID dest) + { + if (!temp.value) { + loadArgumentInRegister(TrustedImmPtr(0), dest); + } else { + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest); + } + } + + void loadArgumentInRegister(Reference temp, RegisterID dest) + { + assert(temp.value); + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest); + } + + void loadArgumentInRegister(ReentryBlock block, RegisterID dest) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); + addPatch(patch, block.block); + } + +#ifdef VALUE_FITS_IN_REGISTER + void loadArgumentInRegister(V4IR::Temp* temp, RegisterID dest) + { + if (!temp) { + QV4::Value undefined = QV4::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + load64(addr, dest); + } + } + + void loadArgumentInRegister(V4IR::Const* c, RegisterID dest) + { + QV4::Value v = convertToValue(c); + move(TrustedImm64(v.val), dest); + } + + void loadArgumentInRegister(V4IR::Expr* expr, RegisterID dest) + { + if (!expr) { + QV4::Value undefined = QV4::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgumentInRegister(expr->asTemp(), dest); + } else if (expr->asConst()) { + loadArgumentInRegister(expr->asConst(), dest); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } +#else + void loadArgumentInRegister(V4IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } +#endif + + void loadArgumentInRegister(QV4::String* string, RegisterID dest) + { + loadArgumentInRegister(TrustedImmPtr(string), dest); + } + + void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + if (imm32.m_value) + move(imm32, dest); + } + + void storeReturnValue(RegisterID dest) + { + move(ReturnValueRegister, dest); + } + +#ifdef VALUE_FITS_IN_REGISTER + void storeReturnValue(const Pointer &dest) + { + store64(ReturnValueRegister, dest); + } + + void storeReturnValue(V4IR::Temp *temp) + { + if (!temp) + return; + Pointer addr = loadTempAddress(ScratchRegister, temp); + storeReturnValue(addr); + } +#endif + + void storeReturnValue(VoidType) + { + } + + template <int StackSlot> + void loadArgumentOnStack(RegisterID reg) + { + poke(reg, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(TrustedImm32 value) + { + poke(value, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(const Pointer& ptr) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(PointerToValue temp) + { + if (temp.value) { + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack<StackSlot>(ptr); + } else { + poke(TrustedImmPtr(0), StackSlot); + } + } + + template <int StackSlot> + void loadArgumentOnStack(Reference temp) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack<StackSlot>(ptr); + } + + template <int StackSlot> + void loadArgumentOnStack(ReentryBlock block) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); + poke(ScratchRegister, StackSlot); + addPatch(patch, block.block); + } + + template <int StackSlot> + void loadArgumentOnStack(TrustedImmPtr ptr) + { + move(TrustedImmPtr(ptr), ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(QV4::String* name) + { + poke(TrustedImmPtr(name), StackSlot); + } + + using JSC::MacroAssembler::loadDouble; + void loadDouble(V4IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, V4IR::Temp* temp) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); + } + + template <typename Result, typename Source> + void copyValue(Result result, Source source); + template <typename Result> + void copyValue(Result result, V4IR::Expr* source); + + void storeValue(QV4::Value value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void storeValue(QV4::Value value, V4IR::Temp* temp); + + static int calculateStackFrameSize(int locals); + void enterStandardStackFrame(int locals); + void leaveStandardStackFrame(int locals); + + template <int argumentNumber, typename T> + void loadArgumentOnStackOrRegister(const T &value) + { + if (argumentNumber < RegisterArgumentCount) + loadArgumentInRegister(value, registerForArgument(argumentNumber)); + else +#if OS(WINDOWS) && CPU(X86_64) + loadArgumentOnStack<argumentNumber>(value); +#else // Sanity: + loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value); +#endif + } + + template <int argumentNumber> + void loadArgumentOnStackOrRegister(const VoidType &value) + { + Q_UNUSED(value); + } + + template <bool selectFirst, int First, int Second> + struct Select + { + enum { Chosen = First }; + }; + + template <int First, int Second> + struct Select<false, First, Second> + { + enum { Chosen = Second }; + }; + + template <int ArgumentIndex, typename Parameter> + struct SizeOnStack + { + enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, QT_POINTER_SIZE, 0>::Chosen }; + }; + + template <int ArgumentIndex> + struct SizeOnStack<ArgumentIndex, VoidType> + { + enum { Size = 0 }; + }; + + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size + + SizeOnStack<1, Arg2>::Size + + SizeOnStack<2, Arg3>::Size + + SizeOnStack<3, Arg4>::Size + + SizeOnStack<4, Arg5>::Size + + SizeOnStack<5, Arg6>::Size + + StackShadowSpace; + + if (stackSpaceNeeded) { + stackSpaceNeeded = WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded); + sub32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + loadArgumentOnStackOrRegister<5>(arg6); + loadArgumentOnStackOrRegister<4>(arg5); + loadArgumentOnStackOrRegister<3>(arg4); + loadArgumentOnStackOrRegister<2>(arg3); + loadArgumentOnStackOrRegister<1>(arg2); + loadArgumentOnStackOrRegister<0>(arg1); + + callAbsolute(functionName, function); + + storeReturnValue(r); + + if (stackSpaceNeeded) + add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) + { + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); + } + + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + QV4::BinOp fallbackImplementation; + QV4::BinOpContext contextImplementation; + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1]; + + void generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp* left, V4IR::Temp* right); + + Jump inline_add32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchAdd32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchAdd32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchSub32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchSub32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchMul32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchMul32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_and32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + and32(addr, reg); +#else + load32(addr, ScratchRegister); + and32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + or32(addr, reg); +#else + load32(addr, ScratchRegister); + or32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + xor32(addr, reg); +#else + load32(addr, ScratchRegister); + xor32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + + void link(QV4::Function *vmFunc); + + void recordLineNumber(int lineNumber); + +private: + V4IR::Function *_function; + QV4::Function *_vmFunction; + QHash<V4IR::BasicBlock *, Label> _addrs; + QHash<V4IR::BasicBlock *, QVector<Jump> > _patches; + QList<CallToLink> _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList<DataLabelPatch> _dataLabelPatches; + + QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches; + V4IR::BasicBlock *_nextBlock; + + QV4::ExecutionEngine *_engine; + + struct CodeLineNumerMapping + { + Assembler::Label location; + int lineNumber; + }; + QVector<CodeLineNumerMapping> codeLineNumberMappings; +}; + +class Q_QML_EXPORT InstructionSelection: + protected V4IR::IRDecoder, + public EvalInstructionSelection +{ +public: + InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(QV4::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + + typedef Assembler::Address Address; + typedef Assembler::Pointer Pointer; + + Address addressForArgument(int index) const + { + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. + return Address(Assembler::StackFrameRegister, (index + 2) * sizeof(void*)); + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) + { + const int index = _function->maxNumberOfArguments - argument; + return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index) + - sizeof(void*) // size of ebp + - sizeof(void*) * Assembler::calleeSavedRegisterCount + ); + } + Pointer baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + + QV4::String *identifier(const QString &s); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + +private: + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(t, function, ...) \ + _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + + int prepareVariableArguments(V4IR::ExprList* args); + + typedef void (*ActivationMethod)(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); + void callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + + uint addLookup(QV4::String *name); + uint addSetterLookup(QV4::String *name); + uint addGlobalLookup(QV4::String *name); + + template <typename Arg1, typename Arg2> + void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) + { + _as->loadPtr(Assembler::Address(Assembler::ContextRegister, offsetof(QV4::ExecutionContext, lookups)), + Assembler::ReturnValueRegister); + + Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); + + Assembler::Address getterSetter = lookupAddr; + getterSetter.offset += getterSetterOffset; + + _as->generateFunctionCallImp(Assembler::Void, "lookup getter/setter", getterSetter, lookupAddr, arg1, arg2); + } + + template <typename Arg1> + void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1) + { + generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType()); + } + + V4IR::BasicBlock *_block; + V4IR::Function* _function; + QV4::Function* _vmFunction; + QVector<QV4::Lookup> _lookups; + Assembler* _as; + QSet<V4IR::BasicBlock*> _reentryBlocks; + int _locals; +}; + +class Q_QML_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } + virtual bool jitCompileRegexps() const + { return true; } +}; + +} // end of namespace MASM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp new file mode 100644 index 0000000000..610f429f5d --- /dev/null +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -0,0 +1,1089 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_util_p.h" +#include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" +#include "qv4ssa_p.h" +#include <private/qv4functionobject_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4debugging_p.h> +#include <private/qv4function_p.h> + +#undef USE_TYPE_INFO + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +namespace { + +inline QV4::BinOp aluOpFunction(V4IR::AluOp op) +{ + switch (op) { + case V4IR::OpInvalid: + return 0; + case V4IR::OpIfTrue: + return 0; + case V4IR::OpNot: + return 0; + case V4IR::OpUMinus: + return 0; + case V4IR::OpUPlus: + return 0; + case V4IR::OpCompl: + return 0; + case V4IR::OpBitAnd: + return QV4::__qmljs_bit_and; + case V4IR::OpBitOr: + return QV4::__qmljs_bit_or; + case V4IR::OpBitXor: + return QV4::__qmljs_bit_xor; + case V4IR::OpAdd: + return 0; + case V4IR::OpSub: + return QV4::__qmljs_sub; + case V4IR::OpMul: + return QV4::__qmljs_mul; + case V4IR::OpDiv: + return QV4::__qmljs_div; + case V4IR::OpMod: + return QV4::__qmljs_mod; + case V4IR::OpLShift: + return QV4::__qmljs_shl; + case V4IR::OpRShift: + return QV4::__qmljs_shr; + case V4IR::OpURShift: + return QV4::__qmljs_ushr; + case V4IR::OpGt: + return QV4::__qmljs_gt; + case V4IR::OpLt: + return QV4::__qmljs_lt; + case V4IR::OpGe: + return QV4::__qmljs_ge; + case V4IR::OpLe: + return QV4::__qmljs_le; + case V4IR::OpEqual: + return QV4::__qmljs_eq; + case V4IR::OpNotEqual: + return QV4::__qmljs_ne; + case V4IR::OpStrictEqual: + return QV4::__qmljs_se; + case V4IR::OpStrictNotEqual: + return QV4::__qmljs_sne; + case V4IR::OpInstanceof: + return 0; + case V4IR::OpIn: + return 0; + case V4IR::OpAnd: + return 0; + case V4IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; +} // anonymous namespace + +// TODO: extend to optimize out temp-to-temp moves, where the lifetime of one temp ends at that statement. +// To handle that, add a hint when such a move will occur, and add a stmt for the hint. +// Then when asked for a register, check if the active statement is the terminating statement, and if so, apply the hint. +// This generalises the hint usage for Phi removal too, when the phi is passed in there as the current statement. +class QQmlJS::Moth::StackSlotAllocator +{ + QHash<V4IR::Temp, int> _slotForTemp; + QHash<V4IR::Temp, int> _hints; + QVector<int> _activeSlots; + + QHash<V4IR::Temp, V4IR::LifeTimeInterval> _intervals; + +public: + StackSlotAllocator(const QList<V4IR::LifeTimeInterval> &ranges, int maxTempCount) + : _activeSlots(maxTempCount) + { + _intervals.reserve(ranges.size()); + foreach (const V4IR::LifeTimeInterval &r, ranges) + _intervals[r.temp()] = r; + } + + void addHint(const V4IR::Temp &hintedSlotOfTemp, const V4IR::Temp &newTemp) + { + if (hintedSlotOfTemp.kind != V4IR::Temp::VirtualRegister + || newTemp.kind != V4IR::Temp::VirtualRegister) + return; + + if (_slotForTemp.contains(newTemp) || _hints.contains(newTemp)) + return; + + int hintedSlot = _slotForTemp.value(hintedSlotOfTemp, -1); + Q_ASSERT(hintedSlot >= 0); + _hints[newTemp] = hintedSlot; + } + + int stackSlotFor(V4IR::Temp *t, V4IR::Stmt *currentStmt) { + int idx = _slotForTemp.value(*t, -1); + if (idx == -1) + idx = allocateSlot(t, currentStmt); + Q_ASSERT(idx >= 0); + return idx; + } + +private: + int allocateSlot(V4IR::Temp *t, V4IR::Stmt *currentStmt) { + const V4IR::LifeTimeInterval &interval = _intervals[*t]; + int idx = _hints.value(*t, -1); + if (idx != -1 && _activeSlots[idx] <= currentStmt->id) { + _slotForTemp[*t] = idx; + _activeSlots[idx] = interval.end(); + return idx; + } + + for (int i = 0, ei = _activeSlots.size(); i != ei; ++i) { + if (_activeSlots[i] < currentStmt->id) { + _slotForTemp[*t] = i; + _activeSlots[i] = interval.end(); + return i; + } + } + + return -1; + } +}; + +InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _vmFunction(0) + , _block(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) + , _stackSlotAllocator(0) + , _currentStatement(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) +{ + V4IR::BasicBlock *block = 0, *nextBlock = 0; + + QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > patches; + QHash<V4IR::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(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(nextBlock, _nextBlock); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + V4IR::Optimizer opt(_function); + opt.run(); + StackSlotAllocator *stackSlotAllocator = 0; + if (opt.isInSSA()) + stackSlotAllocator = new StackSlotAllocator(opt.lifeRanges(), _function->tempCount); + qSwap(_stackSlotAllocator, stackSlotAllocator); + V4IR::Stmt *cs = 0; + qSwap(_currentStatement, cs); + + int locals = frameSize(); + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + + for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + _block = _function->basicBlocks[i]; + _nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; + _addrs.insert(_block, _codeNext - _codeStart); + + foreach (V4IR::Stmt *s, _block->statements) { + if (s->location.isValid()) { + QV4::LineNumberMapping mapping; + mapping.codeOffset = _codeNext - _codeStart; + mapping.lineNumber = s->location.startLine; + _vmFunction->lineNumberMappings.append(mapping); + } + + if (opt.isInSSA() && s->asTerminator()) { + foreach (const V4IR::Optimizer::SSADeconstructionMove &move, + opt.ssaDeconstructionMoves(_block)) { + Q_ASSERT(move.source->asTemp()); // FIXME: support Const exprs in Phi nodes. + if (move.needsConversion()) + convertType(move.source->asTemp(), move.target); + else + copyValue(move.source->asTemp(), move.target); + } + } + + _currentStatement = s; + s->accept(this); + } + } + + // TODO: patch stack size (the push instruction) + patchJumpAddresses(); + + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); + + if (QV4::Debugging::Debugger *debugger = engine()->debugger) + debugger->setPendingBreakpoints(_vmFunction); + + qSwap(_currentStatement, cs); + qSwap(_stackSlotAllocator, stackSlotAllocator); + delete stackSlotAllocator; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(nextBlock, _nextBlock); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.dest = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallProperty call; + call.base = getParam(base); + call.name = identifier(name); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.base = getParam(base); + call.index = getParam(index); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) +{ + if (_stackSlotAllocator) + _stackSlotAllocator->addHint(*source, *target); + + // FIXME: do something more useful with this info + copyValue(source, target); +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, + V4IR::ExprList *args, + V4IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = identifier(*func->id); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateProperty create; + create.base = getParam(base); + create.name = identifier(name); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = getParam(value); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ + Instruction::LoadThis load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(QV4::Value::fromString(identifier(str))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + QV4::Value v = QV4::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + + Instruction::LoadValue load; + load.value = Instr::Param::createValue(v); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = identifier(*name->id); + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + Instruction::StoreName store; + store.source = getParam(source); + store.name = identifier(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + QV4::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + Instruction::LoadProperty load; + load.base = getParam(base); + load.name = identifier(name); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.base = getParam(targetBase); + store.name = identifier(targetName); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + if (_stackSlotAllocator) + _stackSlotAllocator->addHint(*sourceTemp, *targetTemp); + + Instruction::MoveTemp move; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); + if (move.source != move.result) + addInstruction(move); +} + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + QV4::UnaryOpName op = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: op = QV4::__qmljs_not; break; + case V4IR::OpUMinus: op = QV4::__qmljs_uminus; break; + case V4IR::OpUPlus: op = QV4::__qmljs_uplus; break; + case V4IR::OpCompl: op = QV4::__qmljs_compl; break; + case V4IR::OpIncrement: op = QV4::__qmljs_increment; break; + case V4IR::OpDecrement: op = QV4::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); + addInstruction(unop); + } else { + qWarning(" UNOP1"); + } +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ +#ifdef USE_TYPE_INFO + if (leftSource->type & V4IR::NumberType && rightSource->type & V4IR::NumberType) { + // TODO: add Temp+Const variation on the topic. + switch (oper) { + case V4IR::OpAdd: { + Instruction::AddNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + case V4IR::OpMul: { + Instruction::MulNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + case V4IR::OpSub: { + Instruction::SubNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + default: + break; + } + } +#else // !USE_TYPE_INFO + Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); +#endif // USE_TYPE_INFO + + if (oper == V4IR::OpInstanceof || oper == V4IR::OpIn || oper == V4IR::OpAdd) { + Instruction::BinopContext binop; + if (oper == V4IR::OpInstanceof) + binop.alu = QV4::__qmljs_instanceof; + else if (oper == V4IR::OpIn) + binop.alu = QV4::__qmljs_in; + else + binop.alu = QV4::__qmljs_add; + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); + } else { + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); + } +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + QV4::InplaceBinOpName op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_name; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_name; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_name; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_name; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_name; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_name; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_name; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_name; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_name; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_name; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.name = identifier(targetName); + ieo.source = getParam(rightSource); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + QV4::InplaceBinOpElement op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_element; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_element; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_element; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_element; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_element; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_element; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_element; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_element; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_element; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_element; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(source); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + QV4::InplaceBinOpMember op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_member; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_member; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_member; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_member; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_member; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_member; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_member; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_member; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_member; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_member; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.base = getParam(targetBase); + imo.member = identifier(targetName); + imo.source = getParam(source); + addInstruction(imo); +} + +void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, quint32 &args) +{ + bool singleArgIsTemp = false; + if (e && e->next == 0 && e->expr->asTemp()) { + singleArgIsTemp = e->expr->asTemp()->kind == V4IR::Temp::VirtualRegister; + } + + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = getParam(e->expr).index; + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = outgoingArgumentTempStart(); + assert(argLocation >= 0); + argc = 0; + args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + ++argc; + e = e->next; + } + } else { + argc = 0; + args = 0; + } +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + if (s->target == _nextBlock) + return; + + 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(V4IR::CJump *s) +{ + Instr::Param condition; + if (V4IR::Temp *t = s->cond->asTemp()) { + condition = getResultParam(t); + } else if (V4IR::Binop *b = s->cond->asBinop()) { + condition = getResultParam(0); + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; + addInstruction(binop); + } else { + Q_UNIMPLEMENTED(); + } + + Instruction::CJump 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(V4IR::Ret *s) +{ + Instruction::Ret ret; + ret.result = getParam(s->expr); + addInstruction(ret); +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + Instruction::EnterTry enterTry; + enterTry.tryOffset = 0; + enterTry.catchOffset = 0; + enterTry.exceptionVarName = identifier(t->exceptionVarName); + enterTry.exceptionVar = getParam(t->exceptionVar); + ptrdiff_t enterTryLoc = addInstruction(enterTry); + + ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry)); + _patches[t->tryBlock].append(tryLoc); + + ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry)); + _patches[t->catchBlock].append(catchLoc); +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallActivationProperty call; + call.name = identifier(*func->id); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(QV4::Value::fromBoolean(false)); + load.result = getResultParam(result); + addInstruction(load); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + Instruction::CallBuiltinThrow call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + Instruction::CallBuiltinFinishTry call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachIteratorObject call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachNextPropertyName call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + Instruction::CallBuiltinPushScope call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPopScope() +{ + Instruction::CallBuiltinPopScope call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = deletable; + call.varName = identifier(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.object = getParam(object); + call.name = identifier(name); + call.getter = getParam(getter); + call.setter = getParam(setter); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.object = getParam(object); + call.name = identifier(name); + call.value = getParam(value); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + Instruction::CallBuiltinDefineArray call; + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + int argLocation = outgoingArgumentTempStart(); + + QV4::InternalClass *klass = engine()->emptyClass; + V4IR::ExprList *it = args; + while (it) { + V4IR::Name *name = it->expr->asName(); + it = it->next; + + bool isData = it->expr->asConst()->value; + it = it->next; + klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); + + Instruction::MoveTemp move; + move.source = getParam(it->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + + if (!isData) { + it = it->next; + + Instruction::MoveTemp move; + move.source = getParam(it->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + } + + it = it->next; + } + + Instruction::CallBuiltinDefineObjectLiteral call; + call.internalClass = klass; + call.args = outgoingArgumentTempStart(); + call.result = getResultParam(result); + addInstruction(call); +} + +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +{ +#ifdef MOTH_THREADED_INTERPRETER + instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)]; +#else + instr.common.instructionType = type; +#endif + instr.common.breakPoint = 0; + + 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<V4IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt; + for (PatchIt i = _patches.begin(), ei = _patches.end(); 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(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} + +QV4::String *InstructionSelection::identifier(const QString &s) +{ + QV4::String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +Instr::Param InstructionSelection::getParam(V4IR::Expr *e) { + typedef Instr::Param Param; + assert(e); + + if (V4IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (V4IR::Temp *t = e->asTemp()) { + switch (t->kind) { + case V4IR::Temp::Formal: + case V4IR::Temp::ScopedFormal: return Param::createArgument(t->index, t->scope); + case V4IR::Temp::Local: return Param::createLocal(t->index); + case V4IR::Temp::ScopedLocal: return Param::createScopedLocal(t->index, t->scope); + case V4IR::Temp::VirtualRegister: + return Param::createTemp(_stackSlotAllocator ? + _stackSlotAllocator->stackSlotFor(t, _currentStatement) : t->index); + default: + Q_UNIMPLEMENTED(); + return Param(); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } +} diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h new file mode 100644 index 0000000000..9c17245f67 --- /dev/null +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include <private/qv4global_p.h> +#include <private/qv4isel_p.h> +#include <private/qv4isel_util_p.h> +#include <private/qv4jsir_p.h> +#include <private/qv4object_p.h> +#include "qv4instr_moth_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Moth { + +class StackSlotAllocator; + +class Q_QML_EXPORT InstructionSelection: + public V4IR::IRDecoder, + public EvalInstructionSelection +{ +public: + InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(QV4::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + +private: + 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(); + }; + + Instr::Param getParam(V4IR::Expr *e); + + Instr::Param getResultParam(V4IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + + void simpleMove(V4IR::Move *); + void prepareCallArgs(V4IR::ExprList *, quint32 &, quint32 &); + + int outgoingArgumentTempStart() const { return _function->tempCount; } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1; } + + template <int Instr> + inline ptrdiff_t addInstruction(const InstrData<Instr> &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; + + QV4::String *identifier(const QString &s); + + V4IR::Function *_function; + QV4::Function *_vmFunction; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_nextBlock; + + QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > _patches; + QHash<V4IR::BasicBlock *, ptrdiff_t> _addrs; + + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; + + StackSlotAllocator *_stackSlotAllocator; + V4IR::Stmt *_currentStatement; +}; + +class Q_QML_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } + virtual bool jitCompileRegexps() const + { return false; } +}; + +template<int InstrT> +ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data) +{ + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr); +} + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_MOTH_P_H diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp new file mode 100644 index 0000000000..ca8d249f9f --- /dev/null +++ b/src/qml/compiler/qv4isel_p.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugging_p.h" +#include "qv4engine_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" + +#include <QString> + +#include <cassert> + +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace + +using namespace QQmlJS; +using namespace QQmlJS::V4IR; + +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutionEngine *engine, Module *module) + : _engine(engine) + , useFastLookups(true) +{ + assert(engine); + assert(module); + + createFunctionMapping(0, module->rootFunction); + foreach (V4IR::Function *f, module->functions) { + assert(_irToVM.contains(f)); + } +} + +EvalInstructionSelection::~EvalInstructionSelection() +{} + +EvalISelFactory::~EvalISelFactory() +{} + +QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *outer, Function *irFunction) +{ + QV4::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString()); + _irToVM.insert(irFunction, vmFunction); + + vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); + vmFunction->isStrict = irFunction->isStrict; + vmFunction->outer = outer; + vmFunction->isNamedExpression = irFunction->isNamedExpression; + vmFunction->sourceFile = irFunction->sourceFile; + + if (outer) + outer->nestedFunctions.append(vmFunction); + + foreach (const QString *formal, irFunction->formals) + if (formal) + vmFunction->formals.append(_engine->newString(*formal)); + foreach (const QString *local, irFunction->locals) + if (local) + vmFunction->locals.append(_engine->newString(*local)); + + foreach (V4IR::Function *function, irFunction->nestedFunctions) + createFunctionMapping(vmFunction, function); + + return vmFunction; +} + +QV4::Function *EvalInstructionSelection::vmFunction(Function *f) { + QV4::Function *function = _irToVM[f]; + if (!function->code) + run(function, f); + return function; +} + +void IRDecoder::visitMove(V4IR::Move *s) +{ + if (s->op == V4IR::OpInvalid) { + if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + setActivationProperty(s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Temp *t = s->target->asTemp()) { + if (V4IR::Name *n = s->source->asName()) { + if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + loadThisObject(t); + else + getActivationProperty(n, t); + return; + } else if (V4IR::Const *c = s->source->asConst()) { + loadConst(c, t); + return; + } else if (V4IR::Temp *t2 = s->source->asTemp()) { + copyValue(t2, t); + return; + } else if (V4IR::String *str = s->source->asString()) { + loadString(*str->value, t); + return; + } else if (V4IR::RegExp *re = s->source->asRegExp()) { + loadRegexp(re, t); + return; + } else if (V4IR::Closure *clos = s->source->asClosure()) { + initClosure(clos, t); + return; + } else if (V4IR::New *ctor = s->source->asNew()) { + if (Name *func = ctor->base->asName()) { + constructActivationProperty(func, ctor->args, t); + return; + } else if (V4IR::Member *member = ctor->base->asMember()) { + constructProperty(member->base->asTemp(), *member->name, ctor->args, t); + return; + } else if (V4IR::Temp *value = ctor->base->asTemp()) { + constructValue(value, ctor->args, t); + return; + } + } else if (V4IR::Member *m = s->source->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + getProperty(base, *m->name, t); + return; + } + } else if (V4IR::Subscript *ss = s->source->asSubscript()) { + getElement(ss->base->asTemp(), ss->index->asTemp(), t); + return; + } else if (V4IR::Unop *u = s->source->asUnop()) { + if (V4IR::Temp *e = u->expr->asTemp()) { + unop(u->op, e, t); + return; + } + } else if (V4IR::Binop *b = s->source->asBinop()) { + binop(b->op, b->left, b->right, t); + return; + } else if (V4IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callBuiltin(c, t); + return; + } else if (Member *member = c->base->asMember()) { + Q_ASSERT(member->base->asTemp()); + callProperty(member->base->asTemp(), *member->name, c->args, t); + return; + } else if (Subscript *s = c->base->asSubscript()) { + Q_ASSERT(s->base->asTemp()); + Q_ASSERT(s->index->asTemp()); + callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, t); + return; + } else if (V4IR::Temp *value = c->base->asTemp()) { + callValue(value, c->args, t); + return; + } + } else if (V4IR::Convert *c = s->source->asConvert()) { + Q_ASSERT(c->expr->asTemp()); + convertType(c->expr->asTemp(), t); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + if (s->source->asTemp()) { + setProperty(s->source->asTemp(), base, *m->name); + return; + } + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); + return; + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (V4IR::Temp *t = s->target->asTemp()) { + if (s->source->asTemp()) { + binop(s->op, t, s->source->asTemp(), t); + return; + } + } else if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + inplaceNameOp(s->op, s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(), + ss->index->asTemp()); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (s->source->asTemp()) { + inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name); + return; + } + } + } + + // For anything else...: + Q_UNIMPLEMENTED(); + s->dump(qout, V4IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +IRDecoder::~IRDecoder() +{ +} + +void IRDecoder::visitExp(V4IR::Exp *s) +{ + if (V4IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + if (c->base->asName()) { + callBuiltin(c, 0); + } else if (Temp *value = c->base->asTemp()) { + callValue(value, c->args, 0); + } else if (Member *member = c->base->asMember()) { + Q_ASSERT(member->base->asTemp()); + callProperty(member->base->asTemp(), *member->name, c->args, 0); + } else if (Subscript *s = c->base->asSubscript()) { + Q_ASSERT(s->base->asTemp()); + Q_ASSERT(s->index->asTemp()); + callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, 0); + } else { + Q_UNIMPLEMENTED(); + } + } else { + Q_UNIMPLEMENTED(); + } +} + +void IRDecoder::callBuiltin(V4IR::Call *call, V4IR::Temp *result) +{ + V4IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + switch (baseName->builtin) { + case V4IR::Name::builtin_invalid: + callBuiltinInvalid(baseName, call->args, result); + return; + + case V4IR::Name::builtin_typeof: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinTypeofName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinTypeofValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_delete: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinDeleteName(*n->id, result); + return; + } else if (call->args->expr->asTemp()){ + // TODO: should throw in strict mode + callBuiltinDeleteValue(result); + return; + } + } break; + + case V4IR::Name::builtin_postincrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostIncrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostIncrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_postdecrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostDecrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostDecrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_throw: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinThrow(arg); + } return; + + case V4IR::Name::builtin_finish_try: + callBuiltinFinishTry(); + return; + + case V4IR::Name::builtin_foreach_iterator_object: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachIteratorObject(arg, result); + } return; + + case V4IR::Name::builtin_foreach_next_property_name: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachNextPropertyname(arg, result); + } return; + case V4IR::Name::builtin_push_with_scope: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinPushWithScope(arg); + } return; + + case V4IR::Name::builtin_pop_scope: + callBuiltinPopScope(); + return; + + case V4IR::Name::builtin_declare_vars: { + if (!call->args) + return; + V4IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == V4IR::BoolType); + for (V4IR::ExprList *it = call->args->next; it; it = it->next) { + V4IR::Name *arg = it->expr->asName(); + assert(arg != 0); + callBuiltinDeclareVar(deletable->value != 0, *arg->id); + } + } return; + + case V4IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + V4IR::Temp *setter = args->expr->asTemp(); + + callBuiltinDefineGetterSetter(object, *name->id, getter, setter); + } return; + + case V4IR::Name::builtin_define_property: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineProperty(object, *name->id, value); + } return; + + case V4IR::Name::builtin_define_array: + callBuiltinDefineArray(result, call->args); + return; + + case V4IR::Name::builtin_define_object_literal: + callBuiltinDefineObjectLiteral(result, call->args); + return; + + default: + break; + } + + Q_UNIMPLEMENTED(); + call->dump(qout); qout << endl; + assert(!"TODO!"); + Q_UNREACHABLE(); +} diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h new file mode 100644 index 0000000000..965caf2cba --- /dev/null +++ b/src/qml/compiler/qv4isel_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "private/qv4global_p.h" +#include "qv4jsir_p.h" + +#include <qglobal.h> +#include <QHash> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct ExecutionEngine; +struct Function; +} + +namespace QQmlJS { + +class Q_QML_EXPORT EvalInstructionSelection +{ +public: + EvalInstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + virtual ~EvalInstructionSelection() = 0; + + QV4::Function *vmFunction(V4IR::Function *f); + + void setUseFastLookups(bool b) { useFastLookups = b; } + +protected: + QV4::Function *createFunctionMapping(QV4::Function *outer, V4IR::Function *irFunction); + QV4::ExecutionEngine *engine() const { return _engine; } + virtual void run(QV4::Function *vmFunction, V4IR::Function *function) = 0; + +private: + QV4::ExecutionEngine *_engine; + QHash<V4IR::Function *, QV4::Function *> _irToVM; +protected: + bool useFastLookups; +}; + +class Q_QML_EXPORT EvalISelFactory +{ +public: + virtual ~EvalISelFactory() = 0; + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) = 0; + virtual bool jitCompileRegexps() const = 0; +}; + +namespace V4IR { +class Q_QML_EXPORT IRDecoder: protected V4IR::StmtVisitor +{ +public: + virtual ~IRDecoder() = 0; + + virtual void visitPhi(V4IR::Phi *) {} + +public: // visitor methods for StmtVisitor: + virtual void visitMove(V4IR::Move *s); + virtual void visitExp(V4IR::Exp *s); + +public: // to implement by subclasses: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinThrow(V4IR::Temp *arg) = 0; + virtual void callBuiltinFinishTry() = 0; + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0; + virtual void callBuiltinPopScope() = 0; + virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0; + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0; + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target) = 0; + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void loadThisObject(V4IR::Temp *temp) = 0; + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; + virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) = 0; + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName) = 0; + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) = 0; + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) = 0; + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) = 0; + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) = 0; + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) = 0; + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + +private: + void callBuiltin(V4IR::Call *c, V4IR::Temp *temp); +}; +} // namespace IR + +} // namespace QQmlJS + +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 new file mode 100644 index 0000000000..a422150dd0 --- /dev/null +++ b/src/qml/compiler/qv4isel_util_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_UTIL_P_H +#define QV4ISEL_UTIL_P_H + +#include "private/qv4runtime_p.h" +#include "qv4jsir_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +inline QV4::Value convertToValue(V4IR::Const *c) +{ + switch (c->type) { + case V4IR::MissingType: + return QV4::Value::emptyValue(); + case V4IR::NullType: + return QV4::Value::nullValue(); + case V4IR::UndefinedType: + return QV4::Value::undefinedValue(); + case V4IR::BoolType: + return QV4::Value::fromBoolean(c->value != 0); + case V4IR::SInt32Type: + return QV4::Value::fromInt32(int(c->value)); + case V4IR::UInt32Type: + return QV4::Value::fromUInt32(unsigned(c->value)); + case V4IR::DoubleType: + return QV4::Value::fromDouble(c->value); + case V4IR::NumberType: { + int ival = (int)c->value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == c->value && !(c->value == 0 && isNegative(c->value))) { + return QV4::Value::fromInt32(ival); + } else { + return QV4::Value::fromDouble(c->value); + } + } + default: + Q_UNREACHABLE(); + } +} + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_UTIL_P_H diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp new file mode 100644 index 0000000000..7f8d257429 --- /dev/null +++ b/src/qml/compiler/qv4jsir.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4jsir_p.h" +#include <private/qqmljsast_p.h> + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> +#include <QtCore/qset.h> +#include <cmath> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace V4IR { + +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 ObjectType: return QStringLiteral("object"); + default: return QStringLiteral("multiple"); + } +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + 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; + } +} + +struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor +{ + CloneExpr clone; + QSet<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function + Expr *uniqueExpr; + + RemoveSharedExpressions(): uniqueExpr(0) {} + + void operator()(V4IR::Function *function) + { + subexpressions.clear(); + + foreach (BasicBlock *block, function->basicBlocks) { + clone.setBasicBlock(block); + + foreach (Stmt *s, block->statements) { + s->accept(this); + } + } + } + + template <typename _Expr> + _Expr *cleanup(_Expr *expr) + { + if (subexpressions.contains(expr)) { + // the cloned expression is unique by definition + // so we don't need to add it to `subexpressions'. + return clone(expr); + } + + subexpressions.insert(expr); + V4IR::Expr *e = expr; + qSwap(uniqueExpr, e); + expr->accept(this); + qSwap(uniqueExpr, e); + return static_cast<_Expr *>(e); + } + + // statements + virtual void visitExp(Exp *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitMove(Move *s) + { + s->target = cleanup(s->target); + s->source = cleanup(s->source); + } + + virtual void visitJump(Jump *) + { + // nothing to do for Jump statements + } + + virtual void visitCJump(CJump *s) + { + s->cond = cleanup(s->cond); + } + + virtual void visitRet(Ret *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitTry(Try *) + { + // nothing to do for Try statements + } + + virtual void visitPhi(V4IR::Phi *) { Q_UNIMPLEMENTED(); abort(); } + + // expressions + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + + virtual void visitConvert(Convert *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitUnop(Unop *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitBinop(Binop *e) + { + e->left = cleanup(e->left); + e->right = cleanup(e->right); + } + + virtual void visitCall(Call *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitNew(New *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitSubscript(Subscript *e) + { + e->base = cleanup(e->base); + e->index = cleanup(e->index); + } + + virtual void visitMember(Member *e) + { + e->base = cleanup(e->base); + } +}; + +static QString dumpStart(const Expr *e) { + if (e->type == UnknownType) +// return QStringLiteral("**UNKNOWN**"); + return QString(); + else + return typeName(e->type) + QStringLiteral("{"); +} + +static const char *dumpEnd(const Expr *e) { + if (e->type == UnknownType) + return ""; + else + return "}"; +} + +void Const::dump(QTextStream &out) const +{ + if (type != UndefinedType && type != NullType) + out << dumpStart(this); + switch (type) { + case QQmlJS::V4IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::V4IR::NullType: + out << "null"; + break; + case QQmlJS::V4IR::BoolType: + out << (value ? "true" : "false"); + break; + case QQmlJS::V4IR::MissingType: + out << "missing"; + break; + default: + out << QString::number(value, 'g', 16); + break; + } + if (type != UndefinedType && type != NullType) + out << dumpEnd(this); +} + +void String::dump(QTextStream &out) const +{ + out << '"' << escape(*value) << '"'; +} + +QString String::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 += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +void RegExp::dump(QTextStream &out) const +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + +void Name::initGlobal(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = true; + 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->line = line; + this->column = column; +} + +void Name::init(Builtin builtin, quint32 line, quint32 column) +{ + this->id = 0; + this->builtin = builtin; + this->global = false; + this->line = line; + this->column = column; +} + +static 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_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_finish_try: + return "builtin_finish_try"; + case V4IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case V4IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; + case V4IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case V4IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; + case V4IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; + case V4IR::Name::builtin_define_property: + return "builtin_define_property"; + case V4IR::Name::builtin_define_array: + return "builtin_define_array"; + case V4IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; + case V4IR::Name::builtin_define_object_literal: + return "builtin_define_object_literal"; + } + return "builtin_(###FIXME)"; +}; + +void Name::dump(QTextStream &out) const +{ + if (id) + out << *id; + else + out << builtin_to_string(builtin); +} + +void Temp::dump(QTextStream &out) const +{ + out << dumpStart(this); + switch (kind) { + case Formal: out << '#' << index; break; + case ScopedFormal: out << '#' << index + << '@' << scope; break; + case Local: out << '$' << index; break; + case ScopedLocal: out << '$' << index + << '@' << scope; break; + case VirtualRegister: out << '%' << index; break; + default: out << "INVALID"; + } + out << dumpEnd(this); +} + +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; + if (t1.index < t2.index) return true; + if (t1.index > t2.index) return false; + return t1.scope < t2.scope; +} + +void Closure::dump(QTextStream &out) const +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Convert::dump(QTextStream &out) const +{ + out << dumpStart(this); + out << "convert("; + expr->dump(out); + out << ')' << dumpEnd(this); +} + +void Unop::dump(QTextStream &out) const +{ + out << dumpStart(this) << opname(op); + expr->dump(out); + out << dumpEnd(this); +} + +void Binop::dump(QTextStream &out) const +{ + out << dumpStart(this); + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); + out << dumpEnd(this); +} + +void Call::dump(QTextStream &out) const +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void New::dump(QTextStream &out) const +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void Subscript::dump(QTextStream &out) const +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) const +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target->index << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; + else + out << ") goto " << 'L' << iftrue->index << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +void Try::dump(QTextStream &out, Stmt::Mode mode) +{ + out << "try L" << tryBlock->index << "; catch exception in "; + exceptionVar->dump(out); + out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; +} + +void Phi::dump(QTextStream &out, Stmt::Mode mode) +{ + targetTemp->dump(out); + out << " = phi("; + for (int i = 0, ei = incoming.size(); i < ei; ++i) { + if (i > 0) + out << ", "; + if (incoming[i]) + incoming[i]->dump(out); + } + out << ");"; +} + +Function *Module::newFunction(const QString &name, Function *outer) +{ + Function *f = new Function(this, outer, name); + functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } + return f; +} + +Module::~Module() +{ + foreach (Function *f, functions) { + delete f; + } +} + +Function::~Function() +{ + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (V4IR::BasicBlock *b, basicBlocks) + foreach (V4IR::Stmt *s, b->statements) + s->destroyData(); + + qDeleteAll(basicBlocks); + pool = 0; + module = 0; +} + + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode) +{ + BasicBlock *block = new BasicBlock(this, containingLoop); + return mode == InsertBlock ? insertBasicBlock(block) : block; +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +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; +} +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(unsigned index) +{ + Temp *e = function->New<Temp>(); + e->init(Temp::VirtualRegister, index, 0); + return e; +} + +Temp *BasicBlock::ARG(unsigned index, unsigned scope) +{ + Temp *e = function->New<Temp>(); + e->init(scope ? Temp::ScopedFormal : Temp::Formal, index, scope); + return e; +} + +Temp *BasicBlock::LOCAL(unsigned index, unsigned scope) +{ + Temp *e = function->New<Temp>(); + e->init(scope ? Temp::ScopedLocal : Temp::Local, index, scope); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + 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; + } + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New<String>(); + e->init(value); + return e; +} + +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New<RegExp>(); + e->init(value, flags); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->init(function->newString(id), line, column); + return e; +} + +Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->initGlobal(function->newString(id), line, column); + return e; +} + + +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->init(builtin, line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New<Closure>(); + clos->init(function); + return clos; +} + +Expr *BasicBlock::CONVERT(Expr *expr, Type type) +{ + Convert *e = function->New<Convert>(); + e->init(expr, type); + return e; +} + +Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +{ + Unop *e = function->New<Unop>(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + Binop *e = function->New<Binop>(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + 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; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New<New>(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) +{ + Subscript *e = function->New<Subscript>(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Expr *base, const QString *name) +{ + Member*e = function->New<Member>(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + if (isTerminated()) + return 0; + + Exp *s = function->New<Exp>(); + s->init(expr); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + if (isTerminated()) + return 0; + + Move *s = function->New<Move>(); + s->init(target, source, op); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New<Jump>(); + s->init(target); + appendStatement(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + + CJump *s = function->New<CJump>(); + s->init(cond, iftrue, iffalse); + appendStatement(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + + return s; +} + +Stmt *BasicBlock::RET(Temp *expr) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New<Ret>(); + s->init(expr); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) +{ + if (isTerminated()) + return 0; + + Try *t = function->New<Try>(); + t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar); + appendStatement(t); + + assert(! out.contains(tryBlock)); + out.append(tryBlock); + + assert(! out.contains(catchBlock)); + out.append(catchBlock); + + assert(! tryBlock->in.contains(this)); + tryBlock->in.append(this); + + assert(! catchBlock->in.contains(this)); + catchBlock->in.append(this); + + return t; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << index << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + + if (s->location.isValid()) + out << " // line: " << s->location.startLine << " ; column: " << s->location.startColumn; + + out << endl; + } +} + +void BasicBlock::appendStatement(Stmt *statement) +{ + if (nextLocation.isValid()) { + statement->location = nextLocation; + nextLocation = AST::SourceLocation(); + } + statements.append(statement); +} + +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<V4IR::ExprList>(); + clonedList->init(clone(list->expr), clone(list->next)); + return clonedList; +} + +void CloneExpr::visitConst(Const *e) +{ + cloned = block->CONST(e->type, e->value); +} + +void CloneExpr::visitString(String *e) +{ + cloned = block->STRING(e->value); +} + +void CloneExpr::visitRegExp(RegExp *e) +{ + cloned = block->REGEXP(e->value, e->flags); +} + +void CloneExpr::visitName(Name *e) +{ + if (e->id) + cloned = block->NAME(*e->id, e->line, e->column); + else + cloned = block->NAME(e->builtin, e->line, e->column); +} + +void CloneExpr::visitTemp(Temp *e) +{ + Temp *t = block->function->New<Temp>(); + t->init(e->kind, e->index, e->scope); + cloned = t; +} + +void CloneExpr::visitClosure(Closure *e) +{ + cloned = block->CLOSURE(e->value); +} + +void CloneExpr::visitConvert(Convert *e) +{ + cloned = block->CONVERT(clone(e->expr), e->type); +} + +void CloneExpr::visitUnop(Unop *e) +{ + cloned = block->UNOP(e->op, clone(e->expr)); +} + +void CloneExpr::visitBinop(Binop *e) +{ + cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); +} + +void CloneExpr::visitCall(Call *e) +{ + cloned = block->CALL(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitNew(New *e) +{ + cloned = block->NEW(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitSubscript(Subscript *e) +{ + cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); +} + +void CloneExpr::visitMember(Member *e) +{ + cloned = block->MEMBER(clone(e->base), e->name); +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h new file mode 100644 index 0000000000..364ff532c4 --- /dev/null +++ b/src/qml/compiler/qv4jsir_p.h @@ -0,0 +1,890 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.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 <QtCore/QVector> +#include <QtCore/QString> +#include <QtCore/QBitArray> +#include <QtCore/qurl.h> + +#ifdef CONST +#undef CONST +#endif + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QV4 { +struct ExecutionContext; +} + +namespace QQmlJS { + +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + +namespace V4IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct RegExp; +struct Name; +struct Temp; +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 Try; +struct Phi; + +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(V4IR::AluOp op); + +enum Type { + 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, + ObjectType = 1 << 8 +}; +QString typeName(Type t); + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitConvert(Convert *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; + virtual void visitTry(Try *) = 0; + virtual void visitPhi(Phi *) = 0; +}; + +struct Expr { + Type type; + + Expr(): type(UnknownType) {} + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Convert *asConvert() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) const = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out) const; + static QString escape(const QString &s); +}; + +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; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_delete, + builtin_postincrement, + builtin_postdecrement, + builtin_throw, + builtin_finish_try, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name, + builtin_push_with_scope, + builtin_pop_scope, + builtin_declare_vars, + builtin_define_property, + builtin_define_array, + builtin_define_getter_setter, + builtin_define_object_literal + }; + + const QString *id; + Builtin builtin; + bool global; + quint32 line; + quint32 column; + + 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); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Temp: Expr { + enum Kind { + Formal = 0, + ScopedFormal, + Local, + ScopedLocal, + VirtualRegister, + PhysicalRegister, + StackSlot + }; + + unsigned index; + unsigned scope : 29; // how many scopes outside the current one? + unsigned kind : 3; + + 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; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW +{ return t1.index == t2.index && t1.scope == t2.scope && t1.kind == t2.kind; } + +inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW +{ return t.index ^ (t.kind | (t.scope << 3)) ^ seed; } + +bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW; + +struct Closure: Expr { + Function *value; + + void init(Function *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Convert: Expr { + Expr *expr; + + void init(Expr *expr, Type type) + { + this->expr = expr; + this->type = type; + } + + virtual void accept(ExprVisitor *v) { v->visitConvert(this); } + virtual Convert *asConvert() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Unop: Expr { + AluOp op; + Expr *expr; + + void init(AluOp op, Expr *expr) + { + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Binop: Expr { + AluOp op; + Expr *left; // Temp or Const + Expr *right; // Temp or Const + + void init(AluOp op, Expr *left, Expr *right) + { + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Call: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct New: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Subscript: Expr { + Expr *base; + Expr *index; + + void init(Expr *base, Expr *index) + { + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Member: Expr { + Expr *base; + const QString *name; + + void init(Expr *base, const QString *name) + { + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + struct Data { + QVector<unsigned> uses; + QVector<unsigned> defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; + int id; + AST::SourceLocation location; + + Stmt(): d(0), id(-1) {} + virtual ~Stmt() { Q_UNREACHABLE(); } + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual Try *asTry() { return 0; } + virtual Phi *asPhi() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; // LHS - Temp, Name, Member or Subscript + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; // Temp, Binop + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Try: Stmt { + BasicBlock *tryBlock; + BasicBlock *catchBlock; + QString exceptionVarName; + Temp *exceptionVar; // place to store the caught exception, for use when re-throwing + + void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) + { + this->tryBlock = tryBlock; + this->catchBlock = catchBlock; + this->exceptionVarName = exceptionVarName; + this->exceptionVar = exceptionVar; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitTry(this); } + virtual Try *asTry() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Phi: Stmt { + Temp *targetTemp; + QVector<Expr *> incoming; + + virtual void accept(StmtVisitor *v) { v->visitPhi(this); } + virtual Phi *asPhi() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Q_QML_EXPORT Module { + MemoryPool pool; + QVector<Function *> functions; + Function *rootFunction; + + Function *newFunction(const QString &name, Function *outer); + + Module() : rootFunction(0) {} + ~Module(); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector<BasicBlock *> basicBlocks; + int tempCount; + int maxNumberOfArguments; + QSet<QString> strings; + QList<const QString *> formals; + QList<const QString *> locals; + QVector<Function *> nestedFunctions; + Function *outer; + + QString sourceFile; + + int insideWithOrCatch; + + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint isNamedExpression : 1; + uint hasTry: 1; + uint hasWith: 1; + uint unused : 26; + + template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, Function *outer, const QString &name) + : module(module) + , pool(&module->pool) + , tempCount(0) + , maxNumberOfArguments(0) + , outer(outer) + , insideWithOrCatch(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + , hasTry(false) + , hasWith(false) + , unused(0) + { this->name = newString(name); } + + ~Function(); + + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlock *containingLoop, 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)); } + + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void removeSharedExpressions(); + + int indexOfArgument(const QStringRef &string) const; + + bool variablesCanEscape() const + { return hasDirectEval || !nestedFunctions.isEmpty(); } +}; + +struct BasicBlock { + Function *function; + QVector<Stmt *> statements; + QVector<BasicBlock *> in; + QVector<BasicBlock *> out; + QBitArray liveIn; + QBitArray liveOut; + int index; + AST::SourceLocation nextLocation; + + BasicBlock(Function *function, BasicBlock *containingLoop) + : function(function) + , index(-1) + , _containingGroup(containingLoop) + , _groupStart(false) + {} + ~BasicBlock() {} + + template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(unsigned kind); + Temp *ARG(unsigned index, unsigned scope); + Temp *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(Function *function); + + 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); + + Stmt *EXP(Expr *expr); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = V4IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Temp *expr); + Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void appendStatement(Stmt *statement); + + BasicBlock *containingGroup() const + { return _containingGroup; } + + bool isGroupStart() const + { return _groupStart; } + + void markAsGroupStart() + { _groupStart = true; } + +private: + BasicBlock *_containingGroup; + bool _groupStart; +}; + +class CloneExpr: protected V4IR::ExprVisitor +{ +public: + explicit CloneExpr(V4IR::BasicBlock *block = 0); + + void setBasicBlock(V4IR::BasicBlock *block); + + template <typename _Expr> + _Expr *operator()(_Expr *expr) + { + return clone(expr); + } + + template <typename _Expr> + _Expr *clone(_Expr *expr) + { + Expr *c = expr; + qSwap(cloned, c); + expr->accept(this); + qSwap(cloned, c); + return static_cast<_Expr *>(c); + } + +protected: + V4IR::ExprList *clone(V4IR::ExprList *list); + + virtual void visitConst(Const *); + virtual void visitString(String *); + virtual void visitRegExp(RegExp *); + virtual void visitName(Name *); + virtual void visitTemp(Temp *); + virtual void visitClosure(Closure *); + virtual void visitConvert(Convert *); + virtual void visitUnop(Unop *); + virtual void visitBinop(Binop *); + virtual void visitCall(Call *); + virtual void visitNew(New *); + virtual void visitSubscript(Subscript *); + virtual void visitMember(Member *); + +private: + V4IR::BasicBlock *block; + V4IR::Expr *cloned; +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4IR_P_H diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp new file mode 100644 index 0000000000..a139ed9fff --- /dev/null +++ b/src/qml/compiler/qv4ssa.cpp @@ -0,0 +1,2122 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4ssa_p.h" +#include "qv4util_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QLinkedList> +#include <QtCore/QStack> +#include <qv4runtime_p.h> +#include <qv4context_p.h> +#include <cmath> +#include <iostream> +#include <cassert> + +#ifdef CONST +#undef CONST +#endif + +#define QV4_NO_LIVENESS +#undef SHOW_SSA + +QT_USE_NAMESPACE + +using namespace QQmlJS; +using namespace V4IR; + +namespace { + +QTextStream qout(stdout, QIODevice::WriteOnly); + +void showMeTheCode(Function *function) +{ + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + QVector<Stmt *> code; + QHash<Stmt *, BasicBlock *> leader; + + foreach (BasicBlock *block, function->basicBlocks) { + if (block->statements.isEmpty()) + continue; + leader.insert(block->statements.first(), block); + foreach (Stmt *s, block->statements) { + code.append(s); + } + } + + QString name; + if (function->name && !function->name->isEmpty()) + name = *function->name; + else + name.sprintf("%p", function); + + qout << "function " << name << "("; + for (int i = 0; i < function->formals.size(); ++i) { + if (i != 0) + qout << ", "; + qout << *function->formals.at(i); + } + qout << ")" << endl + << "{" << endl; + + foreach (const QString *local, function->locals) { + qout << " var " << *local << ';' << endl; + } + + for (int i = 0; i < code.size(); ++i) { + Stmt *s = code.at(i); + + if (BasicBlock *bb = leader.value(s)) { + qout << endl; + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index)); + str.append(':'); + for (int i = 66 - str.length(); i; --i) + str.append(' '); + qout << str; + qout << "// predecessor blocks:"; + foreach (BasicBlock *in, bb->in) + qout << " L" << in->index; + if (bb->in.isEmpty()) + qout << "(none)"; + if (BasicBlock *container = bb->containingGroup()) + qout << "; container block: L" << container->index; + if (bb->isGroupStart()) + qout << "; group start"; + qout << endl; + } + Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; +// if (n && s->asJump() && s->asJump()->target == leader.value(n)) { +// continue; +// } + + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + if (s->id > 0) + out << s->id << ": "; + s->dump(out, Stmt::MIR); + out.flush(); + + if (s->location.isValid()) + qout << " // line: " << s->location.startLine << " column: " << s->location.startColumn << endl; + +#ifndef QV4_NO_LIVENESS + for (int i = 60 - str.size(); i >= 0; --i) + str.append(' '); + + qout << " " << str; + + // if (! s->uses.isEmpty()) { + // qout << " // uses:"; + // foreach (unsigned use, s->uses) { + // qout << " %" << use; + // } + // } + + // if (! s->defs.isEmpty()) { + // qout << " // defs:"; + // foreach (unsigned def, s->defs) { + // qout << " %" << def; + // } + // } + +# if 0 + if (! s->d->liveIn.isEmpty()) { + qout << " // lives in:"; + for (int i = 0; i < s->d->liveIn.size(); ++i) { + if (s->d->liveIn.testBit(i)) + qout << " %" << i; + } + } +# else + if (! s->d->liveOut.isEmpty()) { + qout << " // lives out:"; + for (int i = 0; i < s->d->liveOut.size(); ++i) { + if (s->d->liveOut.testBit(i)) + qout << " %" << i; + } + } +# endif +#else + qout << " " << str; +#endif + + qout << endl; + + if (n && s->asCJump() /*&& s->asCJump()->iffalse != leader.value(n)*/) { + qout << " else goto L" << s->asCJump()->iffalse->index << ";" << endl; + } + } + + qout << "}" << endl + << endl; + } +} + +class DominatorTree { + int N; + QHash<BasicBlock *, int> dfnum; + QVector<BasicBlock *> vertex; + QHash<BasicBlock *, BasicBlock *> parent; + QHash<BasicBlock *, BasicBlock *> ancestor; + QHash<BasicBlock *, BasicBlock *> best; + QHash<BasicBlock *, BasicBlock *> semi; + QHash<BasicBlock *, BasicBlock *> idom; + QHash<BasicBlock *, BasicBlock *> samedom; + QHash<BasicBlock *, QSet<BasicBlock *> > bucket; + + void DFS(BasicBlock *p, BasicBlock *n) { + if (dfnum[n] == 0) { + dfnum[n] = N; + vertex[N] = n; + parent[n] = p; + ++N; + foreach (BasicBlock *w, n->out) + DFS(n, w); + } + } + + BasicBlock *ancestorWithLowestSemi(BasicBlock *v) { + BasicBlock *a = ancestor[v]; + if (ancestor[a]) { + BasicBlock *b = ancestorWithLowestSemi(a); + ancestor[v] = ancestor[a]; + if (dfnum[semi[b]] < dfnum[semi[best[v]]]) + best[v] = b; + } + return best[v]; + } + + void link(BasicBlock *p, BasicBlock *n) { + ancestor[n] = p; + best[n] = n; + } + + void calculateIDoms(const QVector<BasicBlock *> &nodes) { + Q_ASSERT(nodes.first()->in.isEmpty()); + vertex.resize(nodes.size()); + foreach (BasicBlock *n, nodes) { + dfnum[n] = 0; + semi[n] = 0; + ancestor[n] = 0; + idom[n] = 0; + samedom[n] = 0; + } + + DFS(0, nodes.first()); + Q_ASSERT(N == nodes.size()); // fails with unreachable nodes... + + for (int i = N - 1; i > 0; --i) { + BasicBlock *n = vertex[i]; + BasicBlock *p = parent[n]; + BasicBlock *s = p; + + foreach (BasicBlock *v, n->in) { + BasicBlock *ss; + if (dfnum[v] <= dfnum[n]) + ss = v; + else + ss = semi[ancestorWithLowestSemi(v)]; + if (dfnum[ss] < dfnum[s]) + s = ss; + } + semi[n] = s; + bucket[s].insert(n); + link(p, n); + foreach (BasicBlock *v, bucket[p]) { + BasicBlock *y = ancestorWithLowestSemi(v); + Q_ASSERT(semi[y] == p); + if (semi[y] == semi[v]) + idom[v] = p; + else + samedom[v] = y; + } + bucket[p].clear(); + } + for (int i = 1; i < N; ++i) { + BasicBlock *n = vertex[i]; + Q_ASSERT(ancestor[n] && ((semi[n] && dfnum[ancestor[n]] <= dfnum[semi[n]]) || semi[n] == n)); + Q_ASSERT(bucket[n].isEmpty()); + if (BasicBlock *sdn = samedom[n]) + idom[n] = idom[sdn]; + } + +#ifdef SHOW_SSA + qout << "Immediate dominators:" << endl; + foreach (BasicBlock *to, nodes) { + qout << '\t'; + if (BasicBlock *from = idom.value(to)) + qout << from->index; + else + qout << "(none)"; + qout << " -> " << to->index << endl; + } +#endif // SHOW_SSA + } + + bool dominates(BasicBlock *dominator, BasicBlock *dominated) const { + for (BasicBlock *it = dominated; it; it = idom[it]) { + if (it == dominator) + return true; + } + + return false; + } + + void computeDF(BasicBlock *n) { + if (DF.contains(n)) + return; // TODO: verify this! + + QSet<BasicBlock *> S; + foreach (BasicBlock *y, n->out) + if (idom[y] != n) + S.insert(y); + + /* + * foreach child c of n in the dominator tree + * computeDF[c] + * foreach element w of DF[c] + * if n does not dominate w or if n = w + * S.insert(w) + * DF[n] = S; + */ + foreach (BasicBlock *c, children[n]) { + computeDF(c); + foreach (BasicBlock *w, DF[c]) + if (!dominates(n, w) || n == w) + S.insert(w); + } + DF[n] = S; + +#ifdef SHOW_SSA + qout << "\tDF[" << n->index << "]: {"; + QList<BasicBlock *> SList = S.values(); + for (int i = 0; i < SList.size(); ++i) { + if (i > 0) + qout << ", "; + qout << SList[i]->index; + } + qout << "}" << endl; +#endif // SHOW_SSA +#ifndef QT_NO_DEBUG + foreach (BasicBlock *fBlock, S) { + Q_ASSERT(!dominates(n, fBlock) || fBlock == n); + bool hasDominatedSucc = false; + foreach (BasicBlock *succ, fBlock->in) + if (dominates(n, succ)) + hasDominatedSucc = true; + if (!hasDominatedSucc) { + qout << fBlock->index << " in DF[" << n->index << "] has no dominated predecessors" << endl; + } + Q_ASSERT(hasDominatedSucc); + } +#endif // !QT_NO_DEBUG + } + + QHash<BasicBlock *, QSet<BasicBlock *> > children; + QHash<BasicBlock *, QSet<BasicBlock *> > DF; + +public: + DominatorTree(const QVector<BasicBlock *> &nodes) + : N(0) + { + calculateIDoms(nodes); + + // compute children of n + foreach (BasicBlock *n, nodes) + children[idom[n]].insert(n); + +#ifdef SHOW_SSA + qout << "Dominator Frontiers:" << endl; +#endif // SHOW_SSA + foreach (BasicBlock *n, nodes) + computeDF(n); + } + + QSet<BasicBlock *> operator[](BasicBlock *n) const { + return DF[n]; + } + + BasicBlock *immediateDominator(BasicBlock *bb) const { + return idom[bb]; + } +}; + +class VariableCollector: public StmtVisitor, ExprVisitor { + QHash<Temp, QSet<BasicBlock *> > _defsites; + QHash<BasicBlock *, QSet<Temp> > A_orig; + QSet<Temp> nonLocals; + QSet<Temp> killed; + + BasicBlock *currentBB; + const bool variablesCanEscape; + bool isCollectable(Temp *t) const + { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + // PhysicalRegister and StackSlot can only get inserted later. + Q_ASSERT(!"Invalid temp kind!"); + return false; + } + } + +public: + VariableCollector(Function *function) + : variablesCanEscape(function->variablesCanEscape()) + { +#ifdef SHOW_SSA + qout << "Variables collected:" << endl; +#endif // SHOW_SSA + + foreach (BasicBlock *bb, function->basicBlocks) { + currentBB = bb; + killed.clear(); + killed.reserve(bb->statements.size() / 2); + foreach (Stmt *s, bb->statements) { + s->accept(this); + } + } + +#ifdef SHOW_SSA + qout << "Non-locals:" << endl; + foreach (const Temp &nonLocal, nonLocals) { + qout << "\t"; + nonLocal.dump(qout); + qout << endl; + } + + qout << "end collected variables." << endl; +#endif // SHOW_SSA + } + + QList<Temp> vars() const { + return _defsites.keys(); + } + + QSet<BasicBlock *> defsite(const Temp &n) const { + return _defsites[n]; + } + + QSet<Temp> inBlock(BasicBlock *n) const { + return A_orig[n]; + } + + bool isNonLocal(const Temp &var) const { return nonLocals.contains(var); } + +protected: + virtual void visitPhi(Phi *) {}; + virtual void visitConvert(Convert *e) { e->expr->accept(this); }; + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) { // ### TODO + } + + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(Move *s) { + s->source->accept(this); + + if (Temp *t = s->target->asTemp()) { + if (isCollectable(t)) { +#ifdef SHOW_SSA + qout << '\t'; + t->dump(qout); + qout << " -> L" << currentBB->index << endl; +#endif // SHOW_SSA + + _defsites[*t].insert(currentBB); + A_orig[currentBB].insert(*t); + + // For semi-pruned SSA: + killed.insert(*t); + } + } + } + + virtual void visitTemp(Temp *t) + { + if (isCollectable(t)) + if (!killed.contains(*t)) + nonLocals.insert(*t); + } +}; + +void insertPhiNode(const Temp &a, BasicBlock *y, Function *f) { +#if defined(SHOW_SSA) + qout << "-> inserted phi node for variable "; + a.dump(qout); + qout << " in block " << y->index << endl; +#endif + + Phi *phiNode = f->New<Phi>(); + phiNode->targetTemp = f->New<Temp>(); + phiNode->targetTemp->init(a.kind, a.index, 0); + y->statements.prepend(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, 0); + phiNode->incoming[i] = t; + } +} + +class VariableRenamer: public StmtVisitor, public ExprVisitor +{ + Function *function; + QHash<Temp, QStack<unsigned> > stack; + QSet<BasicBlock *> seen; + + QHash<Temp, unsigned> defCounts; + + const bool variablesCanEscape; + bool isRenamable(Temp *t) const + { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + Q_ASSERT(!"Invalid temp kind!"); + return false; + } + } + int nextFreeTemp() { + const int next = function->tempCount++; +// qDebug()<<"Next free temp:"<<next; + return next; + } + + /* + + Initialization: + for each variable a + count[a] = 0; + stack[a] = empty; + push 0 onto stack + + Rename(n) = + for each statement S in block n [1] + if S not in a phi-function + for each use of some variable x in S + i = top(stack[x]) + replace the use of x with x_i in S + for each definition of some variable a in S + count[a] = count[a] + 1 + i = count[a] + push i onto stack[a] + replace definition of a with definition of a_i in S + for each successor Y of block n [2] + 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 = top(stack[a]) + replace the j-th operand with a_i + for each child X of n [3] + Rename(X) + for each statement S in block n [4] + for each definition of some variable a in S + pop stack[a] + + */ + +public: + VariableRenamer(Function *f) + : function(f) + , variablesCanEscape(f->variablesCanEscape()) + { + if (!variablesCanEscape) { + Temp t; + t.init(Temp::Local, 0, 0); + for (int i = 0, ei = f->locals.size(); i != ei; ++i) { + t.index = i; + stack[t].push(nextFreeTemp()); + } + } + + Temp t; + t.init(Temp::VirtualRegister, 0, 0); + for (int i = 0, ei = f->tempCount; i != ei; ++i) { + t.index = i; + stack[t].push(i); + } + } + + void run() { + foreach (BasicBlock *n, function->basicBlocks) + rename(n); + +#ifdef SHOW_SSA +// qout << "Temp to local mapping:" << endl; +// foreach (int key, tempMapping.keys()) +// qout << '\t' << key << " -> " << tempMapping[key] << endl; +#endif + } + + void rename(BasicBlock *n) { + if (seen.contains(n)) + return; + seen.insert(n); +// qDebug() << "I: L"<<n->index; + + // [1]: + foreach (Stmt *s, n->statements) + s->accept(this); + + QHash<Temp, unsigned> dc = defCounts; + defCounts.clear(); + + // [2]: + foreach (BasicBlock *Y, n->out) { + const int j = Y->in.indexOf(n); + Q_ASSERT(j >= 0 && j < Y->in.size()); + foreach (Stmt *s, Y->statements) { + if (Phi *phi = s->asPhi()) { + Temp *t = phi->incoming[j]->asTemp(); + unsigned newTmp = stack[*t].top(); +// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index; + t->index = newTmp; + t->kind = Temp::VirtualRegister; + } else { + break; + } + } + } + + // [3]: + foreach (BasicBlock *X, n->out) + rename(X); + + // [4]: + for (QHash<Temp, unsigned>::const_iterator i = dc.begin(), ei = dc.end(); i != ei; ++i) { +// qDebug()<<i.key() <<" -> " << i.value(); + for (unsigned j = 0, ej = i.value(); j < ej; ++j) + stack[i.key()].pop(); + } + } + +protected: + virtual void visitTemp(Temp *e) { // only called for uses, not defs + if (isRenamable(e)) { +// qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top(); + e->index = stack[*e].top(); + e->kind = Temp::VirtualRegister; + } + } + + virtual void visitMove(Move *s) { + // uses: + s->source->accept(this); + + // defs: + if (Temp *t = s->target->asTemp()) + renameTemp(t); + else + s->target->accept(this); + } + + void renameTemp(Temp *t) { + if (isRenamable(t)) { + defCounts[*t] = defCounts.value(*t, 0) + 1; + const int newIdx = nextFreeTemp(); + stack[*t].push(newIdx); +// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx; + t->kind = Temp::VirtualRegister; + t->index = newIdx; + } + } + + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); } + + virtual void visitExp(Exp *s) { s->expr->accept(this); } + + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *s) { /* this should never happen */ } + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitSubscript(Subscript *e) { + e->base->accept(this); + e->index->accept(this); + } + + virtual void visitMember(Member *e) { + e->base->accept(this); + } +}; + +void convertToSSA(Function *function, const DominatorTree &df) +{ +#ifdef SHOW_SSA + qout << "Converting function "; + if (function->name) + qout << *function->name; + else + qout << "<no name>"; + qout << " to SSA..." << endl; +#endif // SHOW_SSA + + // Collect all applicable variables: + VariableCollector variables(function); + + // Place phi functions: + QHash<BasicBlock *, QSet<Temp> > A_phi; + foreach (Temp a, variables.vars()) { + if (!variables.isNonLocal(a)) + continue; // for semi-pruned SSA + + QList<BasicBlock *> W = QList<BasicBlock *>::fromSet(variables.defsite(a)); + while (!W.isEmpty()) { + BasicBlock *n = W.first(); + W.removeFirst(); + foreach (BasicBlock *y, df[n]) { + if (!A_phi[y].contains(a)) { + insertPhiNode(a, y, function); + A_phi[y].insert(a); + if (!variables.inBlock(y).contains(a)) + W.append(y); + } + } + } + } + showMeTheCode(function); + + // Rename variables: + VariableRenamer(function).run(); +} + +class DefUsesCalculator: public StmtVisitor, public ExprVisitor { +public: + struct DefUse { + DefUse() + : defStmt(0) + , blockOfStatement(0) + {} + Stmt *defStmt; + BasicBlock *blockOfStatement; + QList<Stmt *> uses; + }; + +private: + const bool _variablesCanEscape; + QHash<Temp, DefUse> _defUses; + QHash<Stmt *, QList<Temp> > _usesPerStatement; + + BasicBlock *_block; + Stmt *_stmt; + + bool isCollectible(Temp *t) const { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !_variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + Q_UNREACHABLE(); + return false; + } + } + + void addUse(Temp *t) { + Q_ASSERT(t); + if (!isCollectible(t)) + return; + + _defUses[*t].uses.append(_stmt); + _usesPerStatement[_stmt].append(*t); + } + + void addDef(Temp *t) { + if (!isCollectible(t)) + return; + + Q_ASSERT(!_defUses.contains(*t) || _defUses.value(*t).defStmt == 0 || _defUses.value(*t).defStmt == _stmt); + + DefUse &defUse = _defUses[*t]; + defUse.defStmt = _stmt; + defUse.blockOfStatement = _block; + } + +public: + DefUsesCalculator(Function *function) + : _variablesCanEscape(function->variablesCanEscape()) + { + foreach (BasicBlock *bb, function->basicBlocks) { + _block = bb; + foreach (Stmt *stmt, bb->statements) { + _stmt = stmt; + stmt->accept(this); + } + } + + QMutableHashIterator<Temp, DefUse> it(_defUses); + while (it.hasNext()) { + it.next(); + if (!it.value().defStmt) + it.remove(); + } + } + + QList<Temp> defs() const { + return _defUses.keys(); + } + + void removeDef(const Temp &var) { + _defUses.remove(var); + } + + void addUses(const Temp &variable, const QList<Stmt *> &newUses) + { _defUses[variable].uses.append(newUses); } + + int useCount(const Temp &variable) const + { return _defUses[variable].uses.size(); } + + Stmt *defStmt(const Temp &variable) const + { return _defUses[variable].defStmt; } + + BasicBlock *defStmtBlock(const Temp &variable) const + { return _defUses[variable].blockOfStatement; } + + void removeUse(Stmt *usingStmt, const Temp &var) + { _defUses[var].uses.removeAll(usingStmt); } + + QList<Temp> usedVars(Stmt *s) const + { return _usesPerStatement[s]; } + + QList<Stmt *> uses(const Temp &var) const + { return _defUses[var].uses; } + + void dump() const + { + foreach (const Temp &var, _defUses.keys()) { + const DefUse &du = _defUses[var]; + var.dump(qout); + qout<<" -> defined in block "<<du.blockOfStatement->index<<", statement: "; + du.defStmt->dump(qout); + qout<<endl<<" uses:"<<endl; + foreach (Stmt *s, du.uses) { + qout<<" ";s->dump(qout);qout<<endl; + } + } + } + +protected: + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) {} + + virtual void visitPhi(Phi *s) { + addDef(s->targetTemp); + foreach (Expr *e, s->incoming) + addUse(e->asTemp()); + } + + virtual void visitMove(Move *s) { + if (Temp *t = s->target->asTemp()) + addDef(t); + else + s->target->accept(this); + + s->source->accept(this); + } + + virtual void visitTemp(Temp *e) { addUse(e); } + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } +}; + +bool hasPhiOnlyUses(Phi *phi, const DefUsesCalculator &defUses, QSet<Phi *> &collectedPhis) +{ + collectedPhis.insert(phi); + foreach (Stmt *use, defUses.uses(*phi->targetTemp)) { + if (Phi *dependentPhi = use->asPhi()) { + if (!collectedPhis.contains(dependentPhi)) { + if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis)) + return false; + } + } else { + return false; + } + } + return true; +} + +void cleanupPhis(DefUsesCalculator &defUses) +{ + QLinkedList<Phi *> phis; + foreach (const Temp &def, defUses.defs()) + if (Phi *phi = defUses.defStmt(def)->asPhi()) + phis.append(phi); + + QSet<Phi *> toRemove; + while (!phis.isEmpty()) { + Phi *phi = phis.first(); + phis.removeFirst(); + if (toRemove.contains(phi)) + continue; + QSet<Phi *> collectedPhis; + if (hasPhiOnlyUses(phi, defUses, collectedPhis)) + toRemove.unite(collectedPhis); + } + + foreach (Phi *phi, toRemove) { + Temp targetVar = *phi->targetTemp; + + BasicBlock *bb = defUses.defStmtBlock(targetVar); + int idx = bb->statements.indexOf(phi); + bb->statements.remove(idx); + + foreach (const Temp &usedVar, defUses.usedVars(phi)) + defUses.removeUse(phi, usedVar); + defUses.removeDef(targetVar); + } +} + +class DeadCodeElimination: public ExprVisitor { + const bool variablesCanEscape; + DefUsesCalculator &_defUses; + QVector<Temp> _worklist; + +public: + DeadCodeElimination(DefUsesCalculator &defUses, Function *function) + : variablesCanEscape(function->variablesCanEscape()) + , _defUses(defUses) + { + _worklist = QVector<Temp>::fromList(_defUses.defs()); + } + + void run() { + while (!_worklist.isEmpty()) { + const Temp v = _worklist.first(); + _worklist.removeFirst(); + + if (_defUses.useCount(v) == 0) { +// qDebug()<<"-"<<v<<"has no uses..."; + Stmt *s = _defUses.defStmt(v); + if (!s) { + _defUses.removeDef(v); + } else if (!hasSideEffect(s)) { +#ifdef SHOW_SSA + qout<<"-- defining stmt for"; + v.dump(qout); + qout<<"has no side effect"<<endl; +#endif + QVector<Stmt *> &stmts = _defUses.defStmtBlock(v)->statements; + int idx = stmts.indexOf(s); + if (idx != -1) + stmts.remove(idx); + foreach (const Temp &usedVar, _defUses.usedVars(s)) { + _defUses.removeUse(s, usedVar); + _worklist.append(usedVar); + } + _defUses.removeDef(v); + } + } + } + +#ifdef SHOW_SSA + qout<<"******************* After dead-code elimination:"; + _defUses.dump(); +#endif + } + +private: + bool _sideEffect; + + bool hasSideEffect(Stmt *s) { + // TODO: check if this can be moved to IR building. + _sideEffect = false; + if (Move *move = s->asMove()) { + if (Temp *t = move->target->asTemp()) { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return true; + case Temp::Local: + if (variablesCanEscape) + return true; + else + break; + case Temp::VirtualRegister: + break; + default: + Q_ASSERT(!"Invalid temp kind!"); + return true; + } + move->source->accept(this); + } else { + return true; + } + } + return _sideEffect; + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *e) { + // 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 != QStringLiteral("this"))) + _sideEffect = true; + } + virtual void visitTemp(Temp *e) { + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { + // we do not have type information yet, so: + _sideEffect = true; + } + + virtual void visitUnop(Unop *e) { + switch (e->op) { + case OpIncrement: + case OpDecrement: + _sideEffect = true; + break; + + default: + break; + } + + if (!_sideEffect) e->expr->accept(this); + } + virtual void visitBinop(Binop *e) { if (!_sideEffect) e->left->accept(this); if (!_sideEffect) e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { + // TODO: see if we can have subscript accesses without side effect + _sideEffect = true; + } + virtual void visitMember(Member *e) { + // TODO: see if we can have member accesses without side effect + _sideEffect = true; + } + virtual void visitCall(Call *e) { + _sideEffect = true; // TODO: there are built-in functions that have no side effect. + } + virtual void visitNew(New *e) { + _sideEffect = true; // TODO: there are built-in types that have no side effect. + } +}; + +class TypeInference: public StmtVisitor, public ExprVisitor { + bool _variablesCanEscape; + const DefUsesCalculator &_defUses; + QHash<Temp, int> _tempTypes; + QSet<Stmt *> _worklist; + struct TypingResult { + int type; + bool fullyTyped; + + TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {} + explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {} + }; + TypingResult _ty; + +public: + TypeInference(const DefUsesCalculator &defUses) + : _defUses(defUses) + , _ty(UnknownType) + {} + + void run(Function *function) { + _variablesCanEscape = function->variablesCanEscape(); + + // TODO: the worklist handling looks a bit inefficient... check if there is something better + _worklist.clear(); + for (int i = 0, ei = function->basicBlocks.size(); i != ei; ++i) { + BasicBlock *bb = function->basicBlocks[i]; + if (i == 0 || !bb->in.isEmpty()) + foreach (Stmt *s, bb->statements) + _worklist.insert(s); + } + + while (!_worklist.isEmpty()) { + QList<Stmt *> worklist = _worklist.values(); + _worklist.clear(); + while (!worklist.isEmpty()) { + Stmt *s = worklist.first(); + worklist.removeFirst(); +#if defined(SHOW_SSA) + qout<<"Typing stmt ";s->dump(qout);qout<<endl; +#endif + + if (!run(s)) { + _worklist.insert(s); +#if defined(SHOW_SSA) + qout<<"Pushing back stmt: "; + s->dump(qout);qout<<endl; + } else { + qout<<"Finished: "; + s->dump(qout);qout<<endl; +#endif + } + } + } + } + +private: + bool run(Stmt *s) { + TypingResult ty; + std::swap(_ty, ty); + s->accept(this); + std::swap(_ty, ty); + return ty.fullyTyped; + } + + TypingResult run(Expr *e) { + TypingResult ty; + std::swap(_ty, ty); + e->accept(this); + std::swap(_ty, ty); + + if (ty.type != UnknownType) + setType(e, ty.type); + return ty; + } + + bool isAlwaysAnObject(Temp *t) { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return true; + case Temp::Local: + return _variablesCanEscape; + default: + return false; + } + } + + void setType(Expr *e, int ty) { + if (Temp *t = e->asTemp()) { +#if defined(SHOW_SSA) + qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <<t->index<< " to "<<typeName(Type(ty)) << " (" << ty << ")" << endl; +#endif + if (isAlwaysAnObject(t)) { + e->type = ObjectType; + } else { + e->type = (Type) ty; + + if (_tempTypes[*t] != ty) { + _tempTypes[*t] = ty; + +#if defined(SHOW_SSA) + foreach (Stmt *s, _defUses.uses(*t)) { + qout << "Pushing back dependent stmt: "; + s->dump(qout); + qout << endl; + } +#endif + + _worklist += QSet<Stmt *>::fromList(_defUses.uses(*t)); + } + } + } else { + e->type = (Type) ty; + } + } + +protected: + virtual void visitConst(Const *e) { _ty = TypingResult(e->type); } + virtual void visitString(String *) { _ty = TypingResult(StringType); } + virtual void visitRegExp(RegExp *) { _ty = TypingResult(ObjectType); } + virtual void visitName(Name *) { _ty = TypingResult(ObjectType); } + virtual void visitTemp(Temp *e) { + if (isAlwaysAnObject(e)) + _ty = TypingResult(ObjectType); + else + _ty = TypingResult(_tempTypes.value(*e, UnknownType)); + setType(e, _ty.type); + } + virtual void visitClosure(Closure *) { _ty = TypingResult(ObjectType); } // TODO: VERIFY THIS! + virtual void visitConvert(Convert *e) { + _ty = run(e->expr); + } + + virtual 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!"); + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + + virtual 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 & StringType || rightTy.type & 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(); + } + } + + virtual 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 = ObjectType; + } + virtual 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 = ObjectType; + } + virtual void visitSubscript(Subscript *e) { + _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; + _ty.type = ObjectType; + } + + virtual void visitMember(Member *e) { + // TODO: for QML, try to do a static lookup + _ty = run(e->base); + _ty.type = ObjectType; + } + + virtual void visitExp(Exp *s) { _ty = run(s->expr); } + virtual void visitMove(Move *s) { + TypingResult sourceTy = run(s->source); + Q_ASSERT(s->op == OpInvalid); + if (Temp *t = s->target->asTemp()) { + setType(t, sourceTy.type); + _ty = sourceTy; + return; + } + + _ty = run(s->target); + _ty.fullyTyped &= sourceTy.fullyTyped; + } + + virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } + virtual void visitCJump(CJump *s) { _ty = run(s->cond); } + virtual void visitRet(Ret *s) { _ty = run(s->expr); } + virtual void visitTry(Try *s) { setType(s->exceptionVar, ObjectType); _ty = TypingResult(MissingType); } + virtual 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]); + _ty.type |= ty.type; + _ty.fullyTyped &= ty.fullyTyped; + } + + // TODO: check & double check the next condition! + if (_ty.type & ObjectType || _ty.type & UndefinedType || _ty.type & NullType) + _ty.type = ObjectType; + else if (_ty.type & NumberType) + _ty.type = DoubleType; + + setType(s->targetTemp, _ty.type); + } +}; + +class TypePropagation: public StmtVisitor, public ExprVisitor { + Type _ty; + + void run(Expr *&e, Type requestedType = UnknownType) { + qSwap(_ty, requestedType); + e->accept(this); + qSwap(_ty, requestedType); + + if (requestedType != UnknownType) + if (e->type != requestedType) + if (requestedType & NumberType) { +// qDebug()<<"adding conversion from"<<typeName(e->type)<<"to"<<typeName(requestedType); + addConversion(e, requestedType); + } + } + + 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() : _ty(UnknownType) {} + + void run(Function *f) { + foreach (BasicBlock *bb, f->basicBlocks) { + _conversions.clear(); + + foreach (Stmt *s, bb->statements) { + _currStmt = s; + s->accept(this); + } + + foreach (const Conversion &conversion, _conversions) { + if (conversion.stmt->asMove() && conversion.stmt->asMove()->source->asTemp()) { + *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType); + } else { + Temp *target = bb->TEMP(bb->newTemp()); + target->type = conversion.targetType; + Expr *convert = bb->CONVERT(*conversion.expr, conversion.targetType); + Move *convCall = f->New<Move>(); + convCall->init(target, convert, OpInvalid); + + Temp *source = bb->TEMP(target->index); + source->type = conversion.targetType; + *conversion.expr = source; + + int idx = bb->statements.indexOf(conversion.stmt); + bb->statements.insert(idx, convCall); + } + } + } + } + +protected: + virtual void visitConst(Const *c) { + if (_ty & NumberType && c->type & NumberType) { + c->type = _ty; + } + } + + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { run(e->expr, e->type); } + virtual void visitUnop(Unop *e) { run(e->expr, e->type); } + virtual 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: + case OpLShift: + case OpRShift: + case OpURShift: + run(e->left, e->type); + run(e->right, e->type); + break; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + if (e->left->type == DoubleType) + run(e->right, DoubleType); + else if (e->right->type == DoubleType) + run(e->left, DoubleType); + else { + run(e->left, e->type); + run(e->right, e->type); + } + break; + + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + break; + + case OpInstanceof: + case OpIn: + run(e->left, e->type); + run(e->right, e->type); + break; + + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + virtual void visitCall(Call *e) { + run(e->base); + for (ExprList *it = e->args; it; it = it->next) + run(it->expr); + } + virtual void visitNew(New *e) { + run(e->base); + for (ExprList *it = e->args; it; it = it->next) + run(it->expr); + } + virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); } + virtual void visitMember(Member *e) { run(e->base); } + virtual void visitExp(Exp *s) { run(s->expr); } + virtual void visitMove(Move *s) { + run(s->target); + run(s->source, s->target->type); + } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { + run(s->cond, BoolType); + } + virtual void visitRet(Ret *s) { run(s->expr); } + virtual void visitTry(Try *) {} + virtual void visitPhi(Phi *s) { + Type ty = s->targetTemp->type; + foreach (Expr *e, s->incoming) + if (e->asConst()) + run(e, ty); + } +}; + +void doEdgeSplitting(Function *f) +{ + const QVector<BasicBlock *> oldBBs = f->basicBlocks; + + foreach (BasicBlock *bb, oldBBs) { + if (bb->in.size() > 1) { + for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) { + BasicBlock *inBB = bb->in[inIdx]; + if (inBB->out.size() > 1) { // this should have been split! +#if defined(SHOW_SSA) + qDebug() << "Splitting edge from block" << inBB->index << "to block" << bb->index; +#endif + + // create the basic block: + BasicBlock *newBB = new BasicBlock(f, bb->containingGroup()); + newBB->index = f->basicBlocks.last()->index + 1; + f->basicBlocks.append(newBB); + Jump *s = f->New<Jump>(); + s->init(bb); + newBB->statements.append(s); + + // rewire the old outgoing edge + int outIdx = inBB->out.indexOf(bb); + inBB->out[outIdx] = newBB; + newBB->in.append(inBB); + + // rewire the old incoming edge + bb->in[inIdx] = newBB; + newBB->out.append(bb); + + // patch the terminator + Stmt *terminator = inBB->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 { + Q_ASSERT(!"Unknown terminator!"); + } + } + } + } + } +} + +QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const DominatorTree &df) +{ + struct I { + const DominatorTree &df; + QHash<BasicBlock *, BasicBlock *> &startEndLoops; + QSet<BasicBlock *> visited; + QVector<BasicBlock *> &sequence; + BasicBlock *currentGroup; + QList<BasicBlock *> postponed; + + I(const DominatorTree &df, QVector<BasicBlock *> &sequence, + QHash<BasicBlock *, BasicBlock *> &startEndLoops) + : df(df) + , sequence(sequence) + , startEndLoops(startEndLoops) + , currentGroup(0) + {} + + void DFS(BasicBlock *bb) { + Q_ASSERT(bb); + if (visited.contains(bb)) + return; + + if (bb->containingGroup() != currentGroup) { + postponed.append(bb); + return; + } + if (bb->isGroupStart()) + currentGroup = bb; + else if (bb->in.size() > 1) + foreach (BasicBlock *inBB, bb->in) + if (!visited.contains(inBB)) + return; + + Q_ASSERT(df.immediateDominator(bb) == 0 || sequence.contains(df.immediateDominator(bb))); + layout(bb); + if (Stmt *terminator = bb->terminator()) { + if (Jump *j = terminator->asJump()) { + Q_ASSERT(bb->out.size() == 1); + DFS(j->target); + } else if (CJump *cj = terminator->asCJump()) { + Q_ASSERT(bb->out.size() == 2); + DFS(cj->iftrue); + DFS(cj->iffalse); + } else if (terminator->asRet()) { + Q_ASSERT(bb->out.size() == 0); + // nothing to do. + } else { + Q_UNREACHABLE(); + } + } else { + Q_UNREACHABLE(); + } + + if (bb->isGroupStart()) { + currentGroup = bb->containingGroup(); + startEndLoops.insert(bb, sequence.last()); + QList<BasicBlock *> p = postponed; + foreach (BasicBlock *pBB, p) + DFS(pBB); + } + } + + void layout(BasicBlock *bb) { + sequence.append(bb); + visited.insert(bb); + postponed.removeAll(bb); + } + }; + + QVector<BasicBlock *> sequence; + sequence.reserve(function->basicBlocks.size()); + QHash<BasicBlock *, BasicBlock *> startEndLoops; + I(df, sequence, startEndLoops).DFS(function->basicBlocks.first()); + qSwap(function->basicBlocks, sequence); + + showMeTheCode(function); + return startEndLoops; +} + +void checkCriticalEdges(QVector<BasicBlock *> basicBlocks) { + foreach (BasicBlock *bb, basicBlocks) { + if (bb && bb->out.size() > 1) { + foreach (BasicBlock *bb2, bb->out) { + if (bb2 && bb2->in.size() > 1) { + qout << "found critical edge between block " + << bb->index << " and block " << bb2->index; + Q_ASSERT(false); + } + } + } + } +} + +void cleanupBasicBlocks(Function *function) +{ +// showMeTheCode(function); + + // remove all basic blocks that have no incoming edges, but skip the entry block + QVector<BasicBlock *> W = function->basicBlocks; + W.removeFirst(); + QSet<BasicBlock *> toRemove; + + while (!W.isEmpty()) { + BasicBlock *bb = W.first(); + W.removeFirst(); + if (toRemove.contains(bb)) + continue; + if (bb->in.isEmpty()) { + foreach (BasicBlock *outBB, bb->out) { + int idx = outBB->in.indexOf(bb); + if (idx != -1) { + outBB->in.remove(idx); + W.append(outBB); + } + } + toRemove.insert(bb); + } + } + + // TODO: merge 2 basic blocks A and B if A has one outgoing edge (to B), B has one incoming + // edge (from A), but not when A has more than 1 incoming edge and B has more than one + // outgoing edge. + + foreach (BasicBlock *bb, toRemove) { + foreach (Stmt *s, bb->statements) + s->destroyData(); + int idx = function->basicBlocks.indexOf(bb); + if (idx != -1) + function->basicBlocks.remove(idx); + delete bb; + } + + // re-number all basic blocks: + for (int i = 0; i < function->basicBlocks.size(); ++i) + function->basicBlocks[i]->index = i; +} + +class InputOutputCollector: protected StmtVisitor, protected ExprVisitor { + const bool variablesCanEscape; + +public: + QList<Temp> inputs; + QList<Temp> outputs; + + InputOutputCollector(bool variablesCanEscape): variablesCanEscape(variablesCanEscape) {} + + void collect(Stmt *s) { + inputs.clear(); + outputs.clear(); + s->accept(this); + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *e) { + switch (e->kind) { + case Temp::Local: + if (!variablesCanEscape) + inputs.append(*e); + break; + + case Temp::VirtualRegister: + inputs.append(*e); + break; + + default: + break; + } + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitMove(Move *s) { + s->source->accept(this); + if (Temp *t = s->target->asTemp()) { + if ((t->kind == Temp::Local && !variablesCanEscape) || t->kind == Temp::VirtualRegister) + outputs.append(*t); + else + s->target->accept(this); + } else { + s->target->accept(this); + } + } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) {} + virtual void visitPhi(Phi *s) { + // Handled separately + } +}; + +/* + * The algorithm is described in: + * + * Linear Scan Register Allocation on SSA Form + * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010 + * + * There is one slight difference w.r.t. the phi-nodes: in the artice, the phi nodes are attached + * to the basic-blocks. Therefore, in the algorithm in the article, the ranges for input parameters + * for phi nodes run from their definition upto the branch instruction into the block with the phi + * node. In our representation, phi nodes are mostly treaded as normal instructions, so we have to + * enlarge the range to cover the phi node itself. + */ +class LifeRanges { + typedef QSet<Temp> LiveRegs; + + QHash<BasicBlock *, LiveRegs> _liveIn; + QHash<Temp, LifeTimeInterval> _intervals; + QList<LifeTimeInterval> _sortedRanges; + +public: + LifeRanges(Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops) + { + int id = 0; + foreach (BasicBlock *bb, function->basicBlocks) { + foreach (Stmt *s, bb->statements) { + if (s->asPhi()) + s->id = id + 1; + else + s->id = ++id; + } + } + + for (int i = function->basicBlocks.size() - 1; i >= 0; --i) { + BasicBlock *bb = function->basicBlocks[i]; + buildIntervals(bb, startEndLoops.value(bb, 0), function->variablesCanEscape()); + } + + _sortedRanges.reserve(_intervals.size()); + for (QHash<Temp, LifeTimeInterval>::const_iterator i = _intervals.begin(), ei = _intervals.end(); i != ei; ++i) { + LifeTimeInterval range = i.value(); + range.setTemp(i.key()); + _sortedRanges.append(range); + } + qSort(_sortedRanges.begin(), _sortedRanges.end(), LifeTimeInterval::lessThan); + } + + QList<LifeTimeInterval> ranges() const { return _sortedRanges; } + + void dump() const + { + qout << "Life ranges:" << endl; + qout << "Intervals:" << endl; + foreach (const LifeTimeInterval &range, _sortedRanges) { + range.dump(); + qout << endl; + } + + foreach (BasicBlock *bb, _liveIn.keys()) { + qout << "L" << bb->index <<" live-in: "; + QList<Temp> live = QList<Temp>::fromSet(_liveIn.value(bb)); + qSort(live); + for (int i = 0; i < live.size(); ++i) { + if (i > 0) qout << ", "; + live[i].dump(qout); + } + qout << endl; + } + } + +private: + void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd, bool variablesCanEscape) + { + LiveRegs live; + foreach (BasicBlock *successor, bb->out) { + live.unite(_liveIn[successor]); + const int bbIndex = successor->in.indexOf(bb); + Q_ASSERT(bbIndex >= 0); + + foreach (Stmt *s, successor->statements) { + if (Phi *phi = s->asPhi()) { + if (Temp *t = phi->incoming[bbIndex]->asTemp()) + live.insert(*t); + } else { + break; + } + } + } + + foreach (const Temp &opd, live) + _intervals[opd].addRange(bb->statements.first(), bb->statements.last()); + + InputOutputCollector collector(variablesCanEscape); + for (int i = bb->statements.size() - 1; i >= 0; --i) { + Stmt *s = bb->statements[i]; + if (Phi *phi = s->asPhi()) { + live.remove(*phi->targetTemp); + continue; + } + collector.collect(s); + foreach (const Temp &opd, collector.outputs) { + _intervals[opd].setFrom(s); + live.remove(opd); + } + foreach (const Temp &opd, collector.inputs) { + _intervals[opd].addRange(bb->statements.first(), s); + live.insert(opd); + } + } + + if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null. + foreach (const Temp &opd, live) + _intervals[opd].addRange(bb->statements.first(), loopEnd->statements.last()); + } + + _liveIn[bb] = live; + } +}; +} // anonymous namespace + +void LifeTimeInterval::setFrom(Stmt *from) { + Q_ASSERT(from && from->id > 0); + + if (_ranges.isEmpty()) // this is the case where there is no use, only a define + _ranges.push_front(Range(from->id, from->id)); + else + _ranges.first().start = from->id; +} + +void LifeTimeInterval::addRange(Stmt *from, Stmt *to) { + Q_ASSERT(from && from->id > 0); + Q_ASSERT(to && to->id > 0); + Q_ASSERT(to->id >= from->id); + + if (_ranges.isEmpty()) { + _ranges.push_front(Range(from->id, to->id)); + return; + } + + Range *p = &_ranges.first(); + if (to->id + 1 >= p->start && p->end + 1 >= from->id) { + p->start = qMin(p->start, from->id); + p->end = qMax(p->end, to->id); + while (_ranges.count() > 1) { + Range *p1 = &_ranges[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.pop_front(); + p = &_ranges.first(); + } + } else { + Q_ASSERT(to->id < p->start); + _ranges.push_front(Range(from->id, to->id)); + } +} + +void LifeTimeInterval::dump() const { + _temp.dump(qout); + qout << ": "; + if (_ranges.isEmpty()) + qout << "(none)"; + for (int i = 0; i < _ranges.size(); ++i) { + if (i > 0) qout << ", "; + qout << _ranges[i].start << " - " << _ranges[i].end; + } + if (_reg != Invalid) + qout << " (register " << _reg << ")"; +} + +bool LifeTimeInterval::lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2) { + if (r1._ranges.first().start == r2._ranges.first().start) + return r1._ranges.last().end < r2._ranges.last().end; + else + return r1._ranges.first().start < r2._ranges.first().start; +} + +void Optimizer::run() +{ +#if defined(SHOW_SSA) + qout << "##### NOW IN FUNCTION " << (_function->name ? qPrintable(*_function->name) : "anonymous!") + << " with " << _function->basicBlocks.size() << " basic blocks." << endl << flush; +#endif + + // Number all basic blocks, so we have nice numbers in the dumps: + for (int i = 0; i < function->basicBlocks.size(); ++i) + function->basicBlocks[i]->index = i; + showMeTheCode(function); + + cleanupBasicBlocks(function); + + function->removeSharedExpressions(); + +// showMeTheCode(function); + + if (!function->hasTry && !function->hasWith) { +// qout << "Starting edge splitting..." << endl; + doEdgeSplitting(function); +// showMeTheCode(function); + + // Calculate the dominator tree: + DominatorTree df(function->basicBlocks); + + convertToSSA(function, df); +// showMeTheCode(function); + +// qout << "Starting def/uses calculation..." << endl; + DefUsesCalculator defUses(function); + +// qout << "Cleaning up phi nodes..." << endl; + cleanupPhis(defUses); +// showMeTheCode(function); + +// qout << "Starting dead-code elimination..." << endl; + DeadCodeElimination(defUses, function).run(); +// showMeTheCode(function); + +// qout << "Running type inference..." << endl; + TypeInference(defUses).run(function); +// showMeTheCode(function); + +// qout << "Doing type propagation..." << endl; + TypePropagation().run(function); +// showMeTheCode(function); + +// qout << "Doing block scheduling..." << endl; + startEndLoops = scheduleBlocks(function, df); +// showMeTheCode(function); + +#ifndef QT_NO_DEBUG + checkCriticalEdges(function->basicBlocks); +#endif + +// qout << "Finished." << endl; + inSSA = true; + } else { + inSSA = false; + } +} + +namespace { +void insertMove(Function *function, BasicBlock *basicBlock, Temp *target, Expr *source) { + if (target->type != source->type) + source = basicBlock->CONVERT(source, target->type); + + Move *s = function->New<Move>(); + s->init(target, source, OpInvalid); + basicBlock->statements.insert(basicBlock->statements.size() - 1, s); +} +} + +/* + * Quick function to convert out of SSA, so we can put the stuff through the ISel phases. This + * has to be replaced by a phase in the specific ISel back-ends and do register allocation at the + * same time. That way the huge number of redundant moves generated by this function are eliminated. + */ +void Optimizer::convertOutOfSSA() { + // We assume that edge-splitting is already done. + foreach (BasicBlock *bb, function->basicBlocks) { + QVector<Stmt *> &stmts = bb->statements; + while (!stmts.isEmpty()) { + Stmt *s = stmts.first(); + if (Phi *phi = s->asPhi()) { + stmts.removeFirst(); + for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) + insertMove(function, bb->in[i], phi->targetTemp, phi->incoming[i]); + } else { + break; + } + } + } +} + +QList<Optimizer::SSADeconstructionMove> Optimizer::ssaDeconstructionMoves(BasicBlock *basicBlock) +{ + QList<SSADeconstructionMove> moves; + + foreach (BasicBlock *outEdge, basicBlock->out) { + int inIdx = outEdge->in.indexOf(basicBlock); + Q_ASSERT(inIdx >= 0); + foreach (Stmt *s, outEdge->statements) { + if (Phi *phi = s->asPhi()) { + SSADeconstructionMove m; + m.source = phi->incoming[inIdx]; + m.target = phi->targetTemp; + moves.append(m); + } else { + break; + } + } + } + + return moves; +} + +QList<LifeTimeInterval> Optimizer::lifeRanges() const +{ + Q_ASSERT(isInSSA()); + + LifeRanges lifeRanges(function, startEndLoops); +// lifeRanges.dump(); +// showMeTheCode(function); + return lifeRanges.ranges(); +} diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h new file mode 100644 index 0000000000..097a40eff1 --- /dev/null +++ b/src/qml/compiler/qv4ssa_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SSA_P_H +#define QV4SSA_P_H + +#include "qv4jsir_p.h" + +QT_BEGIN_NAMESPACE +namespace QQmlJS { +namespace V4IR { + +class LifeTimeInterval { + struct Range { + int start; + int end; + + Range(int start = Invalid, int end = Invalid) + : start(start) + , end(end) + {} + }; + + Temp _temp; + QList<Range> _ranges; + int _reg; + +public: + static const int Invalid = -1; + + LifeTimeInterval() + : _reg(Invalid) + {} + + void setTemp(const Temp &temp) { this->_temp = temp; } + Temp temp() const { return _temp; } + + void setFrom(Stmt *from); + void addRange(Stmt *from, Stmt *to); + + int start() const { return _ranges.first().start; } + int end() const { return _ranges.last().end; } + + int reg() const { return _reg; } + void setReg(int reg) { _reg = reg; } + + void dump() const; + static bool lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2); +}; + +class Optimizer +{ +public: + struct SSADeconstructionMove + { + Expr *source; + Temp *target; + + bool needsConversion() const + { return target->type != source->type; } + }; + +public: + Optimizer(Function *function) + : function(function) + , inSSA(false) + {} + + void run(); + void convertOutOfSSA(); + + bool isInSSA() const + { return inSSA; } + + QList<SSADeconstructionMove> ssaDeconstructionMoves(BasicBlock *basicBlock); + + QList<LifeTimeInterval> lifeRanges() const; + +private: + Function *function; + bool inSSA; + QHash<BasicBlock *, BasicBlock *> startEndLoops; +}; + +} // V4IR namespace +} // QQmlJS namespace +QT_END_NAMESPACE + +#endif // QV4SSA_P_H diff --git a/src/qml/compiler/qv4vme_moth.cpp b/src/qml/compiler/qv4vme_moth.cpp new file mode 100644 index 0000000000..a06ce4139a --- /dev/null +++ b/src/qml/compiler/qv4vme_moth.cpp @@ -0,0 +1,596 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" +#include <private/qv4value_p.h> +#include <private/qv4debugging_p.h> +#include <private/qv4exception_p.h> +#include <private/qv4math_p.h> + +#include <iostream> + +#include "qv4alloca_p.h" + +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + if (context->engine->debugger && (instr.breakPoint || context->engine->debugger->pauseAtNextOpportunity())) \ + context->engine->debugger->maybeBreakAtInstruction(code, instr.breakPoint); \ + Q_UNUSED(instr); \ + TRACE_INSTR(I) + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +#ifdef WITH_STATS +namespace { +struct VMStats { + quint64 paramIsValue; + quint64 paramIsArg; + quint64 paramIsLocal; + quint64 paramIsTemp; + quint64 paramIsScopedLocal; + + VMStats() + : paramIsValue(0) + , paramIsArg(0) + , paramIsLocal(0) + , paramIsTemp(0) + , paramIsScopedLocal(0) + {} + + ~VMStats() + { show(); } + + void show() { + fprintf(stderr, "VM stats:\n"); + fprintf(stderr, " value: %lu\n", paramIsValue); + fprintf(stderr, " arg: %lu\n", paramIsArg); + fprintf(stderr, " local: %lu\n", paramIsLocal); + fprintf(stderr, " temp: %lu\n", paramIsTemp); + fprintf(stderr, " scoped local: %lu\n", paramIsScopedLocal); + } +}; +static VMStats vmStats; +#define VMSTATS(what) ++vmStats.what +} +#else // !WITH_STATS +#define VMSTATS(what) {} +#endif // WITH_STATS + +static inline QV4::Value *getValueRef(QV4::ExecutionContext *context, + QV4::Value* stack, + const Instr::Param ¶m +#if !defined(QT_NO_DEBUG) + , unsigned stackSize +#endif + ) +{ +#ifdef DO_TRACE_INSTR + if (param.isValue()) { + fprintf(stderr, " value %s\n", param.value.toString(context)->toQString().toUtf8().constData()); + } else if (param.isArgument()) { + fprintf(stderr, " argument %d@%d\n", param.index, param.scope); + } else if (param.isLocal()) { + fprintf(stderr, " local %d\n", param.index); + } else if (param.isTemp()) { + fprintf(stderr, " temp %d\n", param.index); + } else if (param.isScopedLocal()) { + fprintf(stderr, " temp %d@%d\n", param.index, param.scope); + } else { + Q_ASSERT(!"INVALID"); + } +#endif // DO_TRACE_INSTR + + if (param.isValue()) { + VMSTATS(paramIsValue); + return const_cast<QV4::Value *>(¶m.value); + } else if (param.isArgument()) { + VMSTATS(paramIsArg); + QV4::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + QV4::CallContext *cc = static_cast<QV4::CallContext *>(c); + const unsigned arg = param.index; + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < cc->argumentCount); + Q_ASSERT(cc->arguments); + return cc->arguments + arg; + } else if (param.isLocal()) { + VMSTATS(paramIsLocal); + const unsigned index = param.index; + QV4::CallContext *c = static_cast<QV4::CallContext *>(context); + Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); + Q_ASSERT(c->locals); + return c->locals + index; + } else if (param.isTemp()) { + VMSTATS(paramIsTemp); + Q_ASSERT(param.index < stackSize); + return stack + param.index; + } else if (param.isScopedLocal()) { + VMSTATS(paramIsScopedLocal); + QV4::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + const unsigned index = param.index; + QV4::CallContext *cc = static_cast<QV4::CallContext *>(c); + Q_ASSERT(index >= 0); + Q_ASSERT(index < cc->variableCount()); + Q_ASSERT(cc->locals); + return cc->locals + index; + } else { + Q_UNIMPLEMENTED(); + return 0; + } +} + +#if defined(QT_NO_DEBUG) +# define VALUE(param) (*VALUEPTR(param)) + +// The non-temp case might need some tweaking for QML: there it would probably be a value instead of a local. +# define VALUEPTR(param) \ + (param.isTemp() ? stack + param.index \ + : (param.isLocal() ? static_cast<QV4::CallContext *>(context)->locals + param.index \ + : getValueRef(context, stack, param))) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif + +QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code, + QV4::Value *stack, unsigned stackSize +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return QV4::Value::undefinedValue(); + } +#endif + + context->interpreterInstructionPointer = &code; + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(MoveTemp) + VALUE(instr.result) = VALUE(instr.source); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadValue) +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); + MOTH_END_INSTR(LoadValue) + + MOTH_BEGIN_INSTR(LoadClosure) + __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreName) + + MOTH_BEGIN_INSTR(LoadElement) + __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); + stackSize = instr.value; + stack = static_cast<QV4::Value *>(alloca(stackSize * sizeof(QV4::Value))); + memset(stack, 0, stackSize * sizeof(QV4::Value)); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(CallValue) +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallElement) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallActivationProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + TRACE(args, "starting at %d, length %d", instr.args, instr.argc); + QV4::Value *args = stack + instr.args; + __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_throw(context, VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(EnterTry) + VALUE(instr.exceptionVar) = QV4::Value::undefinedValue(); + try { + const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset; + run(context, tryCode, stack, stackSize); + code = tryCode; + context->interpreterInstructionPointer = &code; + } catch (QV4::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + try { + QV4::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(instr.exceptionVarName, ex.value(), context); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(catchContext, catchCode, stack, stackSize); + code = catchCode; + context->interpreterInstructionPointer = &code; + context = __qmljs_builtin_pop_scope(catchContext); + } catch (QV4::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(context, catchCode, stack, stackSize); + code = catchCode; + context->interpreterInstructionPointer = &code; + } + } + MOTH_END_INSTR(EnterTry) + + MOTH_BEGIN_INSTR(CallBuiltinFinishTry) + return QV4::Value(); + MOTH_END_INSTR(CallBuiltinFinishTry) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) + + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + __qmljs_builtin_post_increment(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + __qmljs_builtin_post_decrement(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeclareVar) + + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter)); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinDefineProperty) + + MOTH_BEGIN_INSTR(CallBuiltinDefineArray) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); + MOTH_END_INSTR(CallBuiltinDefineArray) + + MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) + QV4::Value *args = stack + instr.args; + __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass); + MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateValue) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + + MOTH_BEGIN_INSTR(Jump) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + uint cond = __qmljs_to_boolean(VALUE(instr.condition)); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Unop) + instr.alu(VALUEPTR(instr.result), VALUE(instr.source)); + MOTH_END_INSTR(Unop) + + MOTH_BEGIN_INSTR(Binop) + instr.alu(VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(BinopContext) + instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); + MOTH_END_INSTR(BinopContext) + + MOTH_BEGIN_INSTR(AddNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::add_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() + rhs.asDouble()); + MOTH_END_INSTR(AddNumberParams) + + MOTH_BEGIN_INSTR(MulNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::mul_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() * rhs.asDouble()); + MOTH_END_INSTR(MulNumberParams) + + MOTH_BEGIN_INSTR(SubNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::sub_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() - rhs.asDouble()); + MOTH_END_INSTR(SubNumberParams) + + MOTH_BEGIN_INSTR(Ret) + QV4::Value &result = VALUE(instr.result); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; + MOTH_END_INSTR(Ret) + + MOTH_BEGIN_INSTR(LoadThis) + VALUE(instr.result) = context->thisObject; + MOTH_END_INSTR(LoadThis) + + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(context, + VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source)); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(context, + VALUE(instr.base), + instr.member, + VALUE(instr.source)); + MOTH_END_INSTR(InplaceMemberOp) + + MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(InplaceNameOp) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + const uchar *code = 0; + VME().run(0, code, 0, 0, &jumpTable); + } + return jumpTable; +} +#endif + +QV4::Value VME::exec(QV4::ExecutionContext *ctxt, const uchar *code) +{ + VME vme; + return vme.run(ctxt, code); +} diff --git a/src/qml/compiler/qv4vme_moth_p.h b/src/qml/compiler/qv4vme_moth_p.h new file mode 100644 index 0000000000..59692500ba --- /dev/null +++ b/src/qml/compiler/qv4vme_moth_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include <private/qv4runtime_p.h> +#include "qv4instr_moth_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Moth { + +class VME +{ +public: + static QV4::Value exec(QV4::ExecutionContext *, const uchar *); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif + +private: + QV4::Value run(QV4::ExecutionContext *, const uchar *&code, + QV4::Value *stack = 0, unsigned stackSize = 0 +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); +}; + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4VME_MOTH_P_H |