diff options
author | Erik Verbruggen <erik.verbruggen@qt.io> | 2018-11-23 12:53:18 +0100 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@qt.io> | 2019-03-22 16:11:39 +0000 |
commit | b92b125455cc01dd48f496d85bdb7a1c96a258fd (patch) | |
tree | f8e65eb2a9b5dbb9b94c1677673caae3ac364530 /src/qml/jit | |
parent | a768780f36a9913d4371c4a61706fc90bbba18c5 (diff) |
V4: Add a lowering pass to the traced JIT
This pass converts high-level operations like e.g. a JSAdd to lower
level operations, like a runtime call. This pass will be extended to
take trace information, which can indicate that it can be lowered to
e.g. an AddInt32.
Change-Id: Ieae8df235217189c90048515e199f7e7c7f220b3
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/jit')
-rw-r--r-- | src/qml/jit/jit.pri | 4 | ||||
-rw-r--r-- | src/qml/jit/qv4lowering.cpp | 200 | ||||
-rw-r--r-- | src/qml/jit/qv4lowering_p.h | 107 | ||||
-rw-r--r-- | src/qml/jit/qv4tracingjit.cpp | 5 |
4 files changed, 315 insertions, 1 deletions
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index 49eb2e8a37..e8d5860498 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -18,7 +18,8 @@ SOURCES += \ $$PWD/qv4node.cpp \ $$PWD/qv4graph.cpp \ $$PWD/qv4graphbuilder.cpp \ - $$PWD/qv4tracingjit.cpp \ + $$PWD/qv4lowering.cpp \ + $$PWD/qv4tracingjit.cpp HEADERS += \ $$PWD/qv4ir_p.h \ @@ -27,4 +28,5 @@ HEADERS += \ $$PWD/qv4node_p.h \ $$PWD/qv4graph_p.h \ $$PWD/qv4graphbuilder_p.h \ + $$PWD/qv4lowering_p.h } 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 diff --git a/src/qml/jit/qv4lowering_p.h b/src/qml/jit/qv4lowering_p.h new file mode 100644 index 0000000000..0b482bc9f0 --- /dev/null +++ b/src/qml/jit/qv4lowering_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QV4LOWERING_P_H +#define QV4LOWERING_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/qqmljsmemorypool_p.h> +#include <private/qv4global_p.h> +#include <private/qv4ir_p.h> +#include <private/qv4util_p.h> +#include <private/qv4node_p.h> +#include <private/qv4graph_p.h> + +QT_REQUIRE_CONFIG(qml_tracing); + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace IR { + +// Lowering replaces JS level operations with lower level ones. E.g. a JSAdd is lowered to an AddI32 +// if both inputs and the output are 32bit integers, or to a runtime call in all other cases. This +// transforms the graph into something that is closer to actual executable code. + + +// Last lowering phase: replace all JSOperations that are left with runtime calls. There is nothing +// smart here, all that should have been done before this phase. +class GenericLowering final +{ + Q_DISABLE_COPY(GenericLowering) + +public: + GenericLowering(Function &f); + + void lower(); + +private: + void replaceWithCall(Node *n); + void replaceWithVarArgsCall(Node *n); + static bool allUsesAsUnboxedBool(Node *n); + + Function &function() + { return m_function; } + + Graph *graph() + { return function().graph(); } + + OperationBuilder *opBuilder() + { return graph()->opBuilder(); } + +private: + Function &m_function; +}; + +} // namespace IR +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4LOWERING_P_H diff --git a/src/qml/jit/qv4tracingjit.cpp b/src/qml/jit/qv4tracingjit.cpp index ded2488905..889c0516d3 100644 --- a/src/qml/jit/qv4tracingjit.cpp +++ b/src/qml/jit/qv4tracingjit.cpp @@ -41,6 +41,7 @@ #include "qv4vme_moth_p.h" #include "qv4graphbuilder_p.h" +#include "qv4lowering_p.h" QT_BEGIN_NAMESPACE @@ -73,6 +74,10 @@ void Moth::runTracingJit(QV4::Function *function) IR::GraphBuilder::buildGraph(&irFunction); irFunction.dump(QStringLiteral("initial IR")); irFunction.verify(); + + IR::GenericLowering(irFunction).lower(); + irFunction.dump(QStringLiteral("after generic lowering")); + irFunction.verify(); } } // QV4 namespace |