diff options
Diffstat (limited to 'src/qml/jit/qv4node_p.h')
-rw-r--r-- | src/qml/jit/qv4node_p.h | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/src/qml/jit/qv4node_p.h b/src/qml/jit/qv4node_p.h new file mode 100644 index 0000000000..679a29764a --- /dev/null +++ b/src/qml/jit/qv4node_p.h @@ -0,0 +1,642 @@ +/**************************************************************************** +** +** 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 QV4NODE_P_H +#define QV4NODE_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/qv4operation_p.h> +#include "qv4util_p.h" + +QT_REQUIRE_CONFIG(qml_tracing); + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace IR { + +class Use +{ + Q_DISABLE_COPY_MOVE(Use) + +public: + Use(Node *user) + : m_user(user) + {} + + ~Use() + { + if (m_input) + removeFromList(); + } + + operator Node *() const { return m_input; } + Node *input() const { return m_input; } + Node *user() const { return m_user; } + + inline void set(Node *newInput); + + void validate() const + { + Q_ASSERT(m_user); + if (m_input) { + if (m_prev != nullptr) + Q_ASSERT(*m_prev == this); + if (m_next) { + Q_ASSERT(m_next->m_input == m_input); + Q_ASSERT(m_next->m_prev == &m_next); + m_next->validate(); + } + } + } + + inline int inputIndex() const; + +protected: + friend class Node; + + void addToList(Use **list) { + validate(); + m_next = *list; + if (m_next) + m_next->m_prev = &m_next; + m_prev = list; + *list = this; + validate(); + } + + void removeFromList() { + validate(); + Use **newPrev = m_prev; + *newPrev = m_next; + m_prev = nullptr; + if (m_next) + m_next->m_prev = newPrev; + m_next = nullptr; + m_input = nullptr; + validate(); + } + +private: + Node *m_input = nullptr; + Node *m_user = nullptr; + Use *m_next = nullptr; + Use **m_prev = nullptr; +}; + +// A node represents an calculation, action, or marker in the graph. Each node has an operation, +// input dependencies and uses. The operation indicates what kind of node it is, e.g.: JSAdd, +// Constant, Region, and so on. Two nodes can have the same operation, but different inputs. +// For example, the expressions 1 + 2 and 3 + 4 will each have a node with an JSAdd operation +// (which is exactly the same operation for both nodes), but the nodes have different inputs (1, and +// 2 in the first expression, while the second operation has 3 and 4 as inputs). +class Node final +{ + Q_DISABLE_COPY_MOVE(Node) + +public: + using Id = uint32_t; + using MemoryPool = QQmlJS::MemoryPool; + class Inputs; + +public: + static Node *create(MemoryPool *pool, Id id, const Operation *op, size_t nInputs, + Node * const *inputs, bool inputsAreExtensible = false); + ~Node() = delete; + + inline bool isDead() const; + inline void kill(); + + Id id() const { return m_id; } + + const Operation *operation() const + { return m_op; } + + void setOperation(const Operation *op) + { m_op = op; } + + Operation::Kind opcode() const + { return operation()->kind(); } + + inline Inputs inputs() const; + void addInput(MemoryPool *pool, Node *in); + void removeInput(unsigned index); + void removeInputs(unsigned start, unsigned count); + void removeAllInputs(); + uint32_t inputCount() const + { return m_nInputs; } + void trimInputCount(unsigned newCount); + + void removeExceptionHandlerUse(); + + Node *input(unsigned idx) const + { + Q_ASSERT(idx < inputCount()); + return m_inputs.at(idx); + } + + Node *effectInput(unsigned effectIndex = 0) const + { + if (operation()->effectInputCount() == 0) + return nullptr; + Q_ASSERT(effectIndex < operation()->effectInputCount()); + return input(operation()->indexOfFirstEffect() + effectIndex); + } + + Node *controlInput(unsigned controlIndex = 0) const + { + if (operation()->controlInputCount() == 0) + return nullptr; + Q_ASSERT(controlIndex < operation()->controlInputCount()); + return input(operation()->indexOfFirstControl() + controlIndex); + } + + Node *frameStateInput() const + { + if (operation()->hasFrameStateInput()) + return input(operation()->indexOfFrameStateInput()); + return nullptr; + } + + void setFrameStateInput(Node *newFramestate) + { + if (operation()->hasFrameStateInput()) + replaceInput(operation()->indexOfFrameStateInput(), newFramestate); + } + + void insertInput(MemoryPool *pool, unsigned index, Node *newInput); + + void replaceInput(Node *oldIn, Node *newIn) + { + for (unsigned i = 0, ei = inputCount(); i != ei; ++i) { + if (input(i) == oldIn) + replaceInput(i, newIn); + } + } + + void replaceInput(unsigned idx, Node *newIn) + { + m_inputs[idx].set(newIn); + } + + class Uses + { + public: + explicit Uses(Node *node) + : m_node(node) + {} + + class const_iterator; + inline const_iterator begin() const; + inline const_iterator end() const; + + bool isEmpty() const; + + private: + Node *m_node; + }; + + Uses uses() { return Uses(this); } + bool hasUses() const { return m_firstUse != nullptr; } + unsigned useCount() const + { + unsigned cnt = 0; + for (Use *it = m_firstUse; it; it = it->m_next) + ++cnt; + return cnt; + } + void replaceAllUsesWith(Node *replacement); + void replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput); + + Node *firstValueUse(); + +private: // types and utility methods + friend class Use; + Node(MemoryPool *pool, Id id, const Operation *op, unsigned nInputs, int capacity); + +private: // fields + Use *m_firstUse = nullptr; + const Operation *m_op = nullptr; + QQmlJS::FixedPoolArray<Use> m_inputs; + unsigned m_nInputs = 0; + Id m_id = 0; +}; + +void Use::set(Node *newInput) +{ + if (m_input) + removeFromList(); + m_input = newInput; + if (newInput) + addToList(&newInput->m_firstUse); +} + +class Node::Inputs final +{ +public: + using value_type = Node *; + + class const_iterator; + inline const_iterator begin() const; + inline const_iterator end() const; + + bool empty() const + { return m_nInputs == 0; } + + unsigned count() const + { return m_nInputs; } + + explicit Inputs(const Use *inputs, unsigned nInputs) + : m_inputs(inputs), m_nInputs(nInputs) + {} + +private: + const Use *m_inputs = nullptr; + unsigned m_nInputs = 0; +}; + +class Node::Inputs::const_iterator final +{ +public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Node *; + using pointer = const value_type *; + using reference = value_type &; + + Node *operator*() const + { return m_inputs->m_input; } + + bool operator==(const const_iterator &other) const + { return m_inputs == other.m_inputs; } + + bool operator!=(const const_iterator &other) const + { return !(*this == other); } + + const_iterator &operator++() + { ++m_inputs; return *this; } + + const_iterator& operator+=(difference_type offset) + { m_inputs += offset; return *this; } + + const_iterator operator+(difference_type offset) const + { return const_iterator(m_inputs + offset); } + + difference_type operator-(const const_iterator &other) const + { return m_inputs - other.m_inputs; } + +private: + friend class Node::Inputs; + + explicit const_iterator(const Use *inputs) + : m_inputs(inputs) + {} + + const Use *m_inputs; +}; + +Node::Inputs::const_iterator Node::Inputs::begin() const +{ return const_iterator(m_inputs); } + +Node::Inputs::const_iterator Node::Inputs::end() const +{ return const_iterator(m_inputs + m_nInputs); } + +Node::Inputs Node::inputs() const +{ + return Inputs(m_inputs.begin(), m_nInputs); +} + +class Node::Uses::const_iterator final +{ +public: + using iterator_category = std::forward_iterator_tag; + using difference_type = int; + using value_type = Node *; + using pointer = Node **; + using reference = Node *&; + + Node *operator*() const + { return m_current->user(); } + + bool operator==(const const_iterator &other) const + { return other.m_current == m_current; } + + bool operator!=(const const_iterator &other) const + { return other.m_current != m_current; } + + const_iterator &operator++() + { m_current = m_next; setNext(); return *this; } + + unsigned inputIndex() const + { return m_current->inputIndex(); } + + bool isUsedAsValue() const + { return inputIndex() < operator*()->operation()->valueInputCount(); } + + bool isUsedAsControl() const + { return operator*()->operation()->indexOfFirstControl() <= inputIndex(); } + +private: + friend class Node::Uses; + + const_iterator() = default; + + explicit const_iterator(Node* node) + : m_current(node->m_firstUse) + { setNext(); } + + void setNext() + { + if (m_current) + m_next = m_current->m_next; + else + m_next = nullptr; + } + +private: + Use *m_current = nullptr; + Use *m_next = nullptr; +}; + +Node::Uses::const_iterator Node::Uses::begin() const +{ return const_iterator(this->m_node); } + +Node::Uses::const_iterator Node::Uses::end() const +{ return const_iterator(); } + +int Use::inputIndex() const +{ + if (!m_user) + return -1; + return int(this - m_user->m_inputs.begin()); +} + +bool Node::isDead() const +{ + Inputs in = inputs(); + return !in.empty() && *in.begin() == nullptr; +} + +void Node::kill() +{ + removeAllInputs(); +} + +class NodeWorkList final +{ + enum State: uint8_t { + Unvisited = 0, + Queued, + Visited, + }; + +public: + NodeWorkList(const Graph *g); + + void reset(); + + bool enqueue(Node *n) + { + State &s = nodeState(n); + if (s == Queued || s == Visited) + return false; + + m_worklist.push_back(n); + s = Queued; + return true; + } + + void enqueue(const std::vector<Node *> &nodes) + { + m_worklist.insert(m_worklist.end(), nodes.begin(), nodes.end()); + for (Node *n : nodes) + nodeState(n) = Queued; + } + + void reEnqueue(Node *n) + { + if (!n) + return; + State &s = nodeState(n); + if (s == Queued) + return; + s = Queued; + m_worklist.push_back(n); + } + + void reEnqueueLate(Node *n) + { + if (!n) + return; + State &s = nodeState(n); + if (s == Queued) + return; + s = Queued; + m_worklist.insert(m_worklist.begin(), n); + } + + void enqueueAllInputs(Node *n) + { + for (Node *input : n->inputs()) + enqueue(input); + } + + void reEnqueueAllInputs(Node *n) + { + for (Node *input : n->inputs()) + reEnqueue(input); + } + + void enqueueValueInputs(Node *n) + { + for (unsigned i = 0, ei = n->operation()->valueInputCount(); i != ei; ++i) + enqueue(n->input(i)); + } + + void enqueueEffectInputs(Node *n) + { + for (unsigned i = n->operation()->indexOfFirstEffect(), ei = n->operation()->effectInputCount(); i != ei; ++i) + enqueue(n->input(i)); + } + + void enqueueAllUses(Node *n) + { + for (Node *use : n->uses()) + enqueue(use); + } + + Node *dequeueNextNodeForVisiting() + { + while (!m_worklist.empty()) { + Node *n = m_worklist.back(); + m_worklist.pop_back(); + State &s = nodeState(n); + if (s == Queued) { + s = Visited; + return n; + } + Q_UNREACHABLE(); + } + + return nullptr; + } + + void markAsVisited(Node *n) + { nodeState(n) = Visited; } + + bool isVisited(Node *n) const + { return nodeState(n) == Visited; } + + bool isEmpty() const + { return m_worklist.empty(); } + + QString status(Node *n) const + { + QString s = QStringLiteral("status for node %1: ").arg(n->id()); + switch (nodeState(n)) { + case Queued: s += QLatin1String("queued"); break; + case Visited: s += QLatin1String("visited"); break; + case Unvisited: s += QLatin1String("unvisited"); break; + } + return s; + } + +private: + State &nodeState(Node *n) + { + const unsigned position(n->id()); + if (position >= m_nodeState.size()) + m_nodeState.resize(position + 1, Unvisited); + + return m_nodeState[position]; + } + + State nodeState(Node *n) const + { return m_nodeState[unsigned(n->id())]; } + +private: + std::vector<Node *> m_worklist; + std::vector<State> m_nodeState; +}; + +class NodeInfo +{ +public: + enum { NoInstructionOffset = -1 }; + +public: + NodeInfo() = default; + + Type type() const { return m_type; } + void setType(Type t) { m_type = t; } + + int currentInstructionOffset() const + { return m_currentInstructionOffset; } + + int nextInstructionOffset() const + { return m_nextInstructionOffset; } + + void setBytecodeOffsets(int current, int next) + { + Q_ASSERT(current != NoInstructionOffset); + Q_ASSERT(next != NoInstructionOffset); + m_currentInstructionOffset = current; + m_nextInstructionOffset = next; + } + +private: + Type m_type; + int m_currentInstructionOffset = NoInstructionOffset; + int m_nextInstructionOffset = NoInstructionOffset; +}; + +class NodeCollector +{ +public: + NodeCollector(const Graph *g, bool collectUses = false, bool skipFramestate = false); + + const std::vector<Node *> &reachable() const + { return m_reachable; } + + void sortById() + { + std::sort(m_reachable.begin(), m_reachable.end(), [](Node *n1, Node *n2) { + return n1->id() < n2->id(); + }); + } + + bool isReachable(Node::Id nodeId) const + { + if (nodeId >= Node::Id(m_isReachable.size())) + return false; + return m_isReachable.at(int(nodeId)); + } + + void markReachable(Node *node) + { + auto nodeId = node->id(); + m_reachable.push_back(node); + if (nodeId >= Node::Id(m_isReachable.size())) + m_isReachable.resize(int(nodeId + 1), false); + m_isReachable.setBit(int(nodeId)); + } + +private: + std::vector<Node *> m_reachable; + BitVector m_isReachable; +}; + +} // namespace IR +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4NODE_P_H |