aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@digia.com>2013-08-08 09:20:52 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-08-08 12:22:30 +0200
commit3288b87e2f75278c7415fbc7c4574bcf7da71295 (patch)
tree95df1e3f0eb76419e780da08b24fae28dc68c580 /src/qml/compiler
parent2d9262a4c6680e3818bf7c07a4d1cfa32b4c9dfe (diff)
Restructure source code
Move the v4 engine classes from a subdir of qml/qml into two subdirs (compiler and jsruntime) of the qml module Remove an unsued qv4syntaxchecker class, and move the moth code directly into compiler. Change-Id: I6929bede1f25098e6cb2e68087e779fac16b0c68 Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri27
-rw-r--r--src/qml/compiler/qv4codegen.cpp2605
-rw-r--r--src/qml/compiler/qv4codegen_p.h451
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp56
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h621
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp1466
-rw-r--r--src/qml/compiler/qv4isel_masm_p.h939
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp1089
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h201
-rw-r--r--src/qml/compiler/qv4isel_p.cpp440
-rw-r--r--src/qml/compiler/qv4isel_p.h165
-rw-r--r--src/qml/compiler/qv4isel_util_p.h87
-rw-r--r--src/qml/compiler/qv4jsir.cpp1024
-rw-r--r--src/qml/compiler/qv4jsir_p.h890
-rw-r--r--src/qml/compiler/qv4ssa.cpp2122
-rw-r--r--src/qml/compiler/qv4ssa_p.h127
-rw-r--r--src/qml/compiler/qv4vme_moth.cpp596
-rw-r--r--src/qml/compiler/qv4vme_moth_p.h76
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 &param
+#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 *>(&param.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