aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit/qv4lowering.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jit/qv4lowering.cpp')
-rw-r--r--src/qml/jit/qv4lowering.cpp200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/qml/jit/qv4lowering.cpp b/src/qml/jit/qv4lowering.cpp
new file mode 100644
index 0000000000..3b3711e7fa
--- /dev/null
+++ b/src/qml/jit/qv4lowering.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QLoggingCategory>
+
+#include "qv4lowering_p.h"
+#include "qv4graph_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLower, "qt.v4.ir.lowering")
+
+GenericLowering::GenericLowering(Function &f)
+ : m_function(f)
+{}
+
+void GenericLowering::lower()
+{
+ NodeWorkList worklist(graph());
+ // The order doesn't really matter for generic lowering, as long as it's done in 1 pass, and
+ // have any clean-up done afterwards.
+ worklist.enqueueAllInputs(graph()->endNode());
+
+ while (Node *n = worklist.dequeueNextNodeForVisiting()) {
+ worklist.enqueueAllInputs(n);
+
+ if (!CallPayload::isRuntimeCall(n->opcode()))
+ continue;
+
+ if (CallPayload::isVarArgsCall(n->opcode()))
+ replaceWithVarArgsCall(n);
+ else
+ replaceWithCall(n);
+ }
+}
+
+void GenericLowering::replaceWithCall(Node *n)
+{
+ auto newOp = opBuilder()->getCall(n->opcode());
+
+ QVarLengthArray<Node *, 32> args;
+ if (CallPayload::takesEngineAsArg(n->opcode(), 0))
+ args.append(graph()->engineNode());
+ if (CallPayload::takesFunctionAsArg(n->opcode(), args.size()))
+ args.append(graph()->functionNode());
+ if (CallPayload::takesFrameAsArg(n->opcode(), args.size()))
+ args.append(graph()->cppFrameNode());
+ const int extraLeadingArguments = args.size();
+
+ for (unsigned arg = 0, earg = n->inputCount(); arg != earg; ++arg) {
+ Node *input = n->input(arg);
+ if (input->opcode() == Meta::FrameState)
+ continue;
+
+ if (arg >= n->operation()->valueInputCount()) {
+ // effect or control input
+ args.append(input);
+ continue;
+ }
+
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), args.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+
+ args.append(input);
+ }
+
+ Node *newCall = graph()->createNode(newOp, args.data(), args.size());
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ function().nodeInfo(newCall)->setType(CallPayload::returnType(n->opcode()));
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+
+ for (Node *use : newCall->uses()) {
+ // fix-up indices for SelectOutput:
+ if (use->opcode() == Meta::SelectOutput) {
+ const int oldIndex = ConstantPayload::get(*use->input(1)->operation())->value().int_32();
+ const int newIndex = oldIndex + extraLeadingArguments;
+ use->replaceInput(1, graph()->createConstantIntNode(newIndex));
+ use->replaceInput(2, newCall->input(newIndex));
+ break;
+ }
+ }
+}
+
+void GenericLowering::replaceWithVarArgsCall(Node *n)
+{
+ const bool isTailCall = n->opcode() == Meta::JSTailCall;
+ Operation *newOp = isTailCall ? opBuilder()->getTailCall()
+ : opBuilder()->getCall(n->opcode());
+
+ //### optimize this for 0 and 1 argument: we don't need to create a VarArgs array for these cases
+
+ const unsigned varArgsStart = CallPayload::varArgsStart(n->opcode()) - 1; // subtract 1 because the runtime calls all take the engine argument as arg0, which isn't in the graph before lowering.
+ Node *vaAlloc = graph()->createNode(
+ opBuilder()->get<Meta::VAAlloc>(),
+ graph()->createConstantIntNode(n->operation()->valueInputCount() - varArgsStart),
+ n->effectInput());
+ QVarLengthArray<Node *, 32> vaSealIn;
+ vaSealIn.append(vaAlloc);
+ for (unsigned i = varArgsStart, ei = n->operation()->valueInputCount(); i != ei; ++i) {
+ vaSealIn.append(graph()->createNode(opBuilder()->get<Meta::VAStore>(), vaAlloc,
+ graph()->createConstantIntNode(vaSealIn.size() - 1),
+ n->input(i)));
+ }
+ vaSealIn.append(vaAlloc);
+ Node *vaSeal = graph()->createNode(opBuilder()->getVASeal(vaSealIn.size() - 2),
+ vaSealIn.data(),
+ vaSealIn.size());
+ QVarLengthArray<Node *, 8> callArgs;
+ if (isTailCall)
+ callArgs.append(graph()->cppFrameNode());
+ callArgs.append(graph()->engineNode());
+ for (unsigned i = 0; i != varArgsStart; ++i) {
+ Node *input = n->input(i);
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), callArgs.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+ callArgs.append(input);
+ }
+ callArgs.append(vaSeal); // args
+ if (n->opcode() != Meta::JSCreateClass) // JSCreateClass is the odd duck
+ callArgs.append(graph()->createConstantIntNode(vaSealIn.size() - 2)); // argc
+ callArgs.append(vaSeal); // effect
+ callArgs.append(n->controlInput(0)); // control flow
+ Node *newCall = graph()->createNode(newOp, callArgs.data(), unsigned(callArgs.size()));
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+}
+
+bool GenericLowering::allUsesAsUnboxedBool(Node *n)
+{
+ for (Node *use : n->uses()) {
+ if (use->operation()->kind() != Meta::Branch)
+ return false;
+ }
+
+ return true;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE