diff options
Diffstat (limited to 'src/qml/jit/qv4mi_p.h')
-rw-r--r-- | src/qml/jit/qv4mi_p.h | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/src/qml/jit/qv4mi_p.h b/src/qml/jit/qv4mi_p.h new file mode 100644 index 0000000000..f976d1dc94 --- /dev/null +++ b/src/qml/jit/qv4mi_p.h @@ -0,0 +1,627 @@ +/**************************************************************************** +** +** 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 QV4MI_P_H +#define QV4MI_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/qv4ir_p.h> +#include <private/qv4node_p.h> +#include <private/qv4operation_p.h> + +#include <llvm/ADT/iterator.h> +#include <llvm/ADT/iterator_range.h> +#include <llvm/ADT/ilist.h> +#include <llvm/ADT/ilist_node.h> + +QT_REQUIRE_CONFIG(qml_tracing); + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace IR { + +// This file contains the Machine Interface (MI) data structures, on which ultimately the assembler +// will operate: + +class MIFunction; // containing all basic blocks, and a reference to the IR function + +class MIBlock; // containing an ordered sequence of instructions + +class MIInstr; // containing operands, and a reference to the IR node, that indicates which + // operation is represented by an instruction + +class MIOperand; // contains a description of where to get/put the input/result of an operation + +// A detail about the stack slots: there two stacks, the JS stack and the native stack. A frame on +// the native stack is divided in two parts: the quad-word part and the double-word part. The +// qword part holds 64bit values, like doubles, and pointers on 64bit architectures. The dword part +// holds 32bit values, like int32s, booleans, and pointers on 32bit architectures. We need to know +// the type of value a slot holds, because if we have to move it to the JS stack, we have to box it +// correctly. +class MIOperand final +{ +public: + enum Kind { + Invalid = 0, + Constant, + VirtualRegister, + + EngineRegister, + CppFrameRegister, + Function, + + JSStackSlot, + BoolStackSlot, + + JumpTarget, + }; + + using List = QQmlJS::FixedPoolArray<MIOperand>; + +public: + MIOperand() = default; + + static MIOperand createConstant(Node *irNode) + { + MIOperand op; + op.m_kind = Constant; + op.m_irNode = irNode; + return op; + } + + static MIOperand createVirtualRegister(Node *irNode, unsigned vreg) + { + MIOperand op; + op.m_kind = VirtualRegister; + op.m_irNode = irNode; + op.m_vreg = vreg; + return op; + } + + static MIOperand createEngineRegister(Node *irNode) + { + MIOperand op; + op.m_kind = EngineRegister; + op.m_irNode = irNode; + return op; + } + + static MIOperand createCppFrameRegister(Node *irNode) + { + MIOperand op; + op.m_kind = CppFrameRegister; + op.m_irNode = irNode; + return op; + } + + static MIOperand createFunction(Node *irNode) + { + MIOperand op; + op.m_kind = Function; + op.m_irNode = irNode; + return op; + } + + static MIOperand createJSStackSlot(Node *irNode, unsigned slot) + { + MIOperand op; + op.m_kind = JSStackSlot; + op.m_irNode = irNode; + op.m_slot = slot; + return op; + } + + static MIOperand createBoolStackSlot(Node *irNode, unsigned slot) + { + MIOperand op; + op.m_kind = BoolStackSlot; + op.m_irNode = irNode; + op.m_slot = slot; + return op; + } + + //### or name this createDeoptBlock? + static MIOperand createJumpTarget(Node *irNode, MIBlock *targetBlock) + { + MIOperand op; + op.m_kind = JumpTarget; + op.m_irNode = irNode; + op.m_targetBlock = targetBlock; + return op; + } + + Kind kind() const + { return m_kind; } + + bool isValid() const + { return m_kind != Invalid; } + + bool isConstant() const + { return m_kind == Constant; } + + bool isVirtualRegister() const + { return kind() == VirtualRegister; } + + bool isEngineRegister() const + { return kind() == EngineRegister; } + + bool isCppFrameRegister() const + { return kind() == CppFrameRegister; } + + bool isFunction() const + { return kind() == Function; } + + bool isJSStackSlot() const + { return kind() == JSStackSlot; } + + bool isBoolStackSlot() const + { return kind() == BoolStackSlot; } + + bool isStackSlot() const + { return isJSStackSlot() || isDWordSlot() || isQWordSlot(); } + + bool isJumpTarget() const + { return kind() == JumpTarget; } + + Node *irNode() const + { return m_irNode; } + + inline Type nodeType(MIFunction *f) const; + + QString debugString() const; + + QV4::Value constantValue() const + { + Q_ASSERT(isConstant()); + if (irNode()->opcode() == Meta::Undefined) + return QV4::Value::undefinedValue(); + if (irNode()->opcode() == Meta::Empty) + return QV4::Value::emptyValue(); + return ConstantPayload::get(*irNode()->operation())->value(); + } + + unsigned virtualRegister() const + { Q_ASSERT(isVirtualRegister()); return m_vreg; } + + unsigned stackSlot() const + { Q_ASSERT(isStackSlot()); return m_slot; } + + MIBlock *targetBlock() const + { Q_ASSERT(isJumpTarget()); return m_targetBlock; } + + inline bool operator==(const MIOperand &other) const + { + if (kind() != other.kind()) + return false; + + if (isStackSlot()) + return stackSlot() == other.stackSlot(); + + switch (kind()) { + case MIOperand::Invalid: + return !other.isValid(); + case MIOperand::Constant: + return constantValue().asReturnedValue() == other.constantValue().asReturnedValue(); + case MIOperand::VirtualRegister: + return virtualRegister() == other.virtualRegister(); + case MIOperand::JumpTarget: + return targetBlock() == other.targetBlock(); + default: + Q_UNREACHABLE(); + return false; + } + } + + bool isDWordSlot() const + { + switch (kind()) { + case BoolStackSlot: + return true; + default: + return false; + } + } + + bool isQWordSlot() const + { + switch (kind()) { + //### TODO: double slots + default: + return false; + } + } + + bool overlaps(const MIOperand &other) const + { + if ((isDWordSlot() && other.isDWordSlot()) || (isQWordSlot() && other.isQWordSlot())) + ; // fine, these are the same + else if (kind() != other.kind()) + return false; + + if (isStackSlot()) + return stackSlot() == other.stackSlot(); + + return false; + } + +private: + Node *m_irNode = nullptr; + union { + unsigned m_vreg; + unsigned m_slot; + MIBlock *m_targetBlock = nullptr; + }; + Kind m_kind = Invalid; +}; + +template <typename NodeTy> struct MIInstrListParentType {}; +template <> struct MIInstrListParentType<MIInstr> { using type = MIBlock; }; + +template <typename NodeTy> class MIInstrList; + +template <typename MISubClass> +class MIInstrListTraits : public llvm::ilist_noalloc_traits<MISubClass> +{ +protected: + using ListTy = MIInstrList<MISubClass>; + using iterator = typename llvm::simple_ilist<MISubClass>::iterator; + using ItemParentClass = typename MIInstrListParentType<MISubClass>::type; + +public: + MIInstrListTraits() = default; + +protected: + void setListOwner(ItemParentClass *listOwner) + { m_owner = listOwner; } + +private: + ItemParentClass *m_owner = nullptr; + + /// getListOwner - Return the object that owns this list. If this is a list + /// of instructions, it returns the BasicBlock that owns them. + ItemParentClass *getListOwner() const { + return m_owner; + } + + static ListTy &getList(ItemParentClass *Par) { + return Par->*(Par->getSublistAccess()); + } + + static MIInstrListTraits<MISubClass> *getSymTab(ItemParentClass *Par) { + return Par ? toPtr(Par->getValueSymbolTable()) : nullptr; + } + +public: + void addNodeToList(MISubClass *V) { V->setParent(getListOwner()); } + void removeNodeFromList(MISubClass *V) { V->setParent(nullptr); } + void transferNodesFromList(MIInstrListTraits &L2, iterator first, + iterator last); +}; + +template <class T> +class MIInstrList: public llvm::iplist_impl<llvm::simple_ilist<T>, MIInstrListTraits<T>> +{ +public: + MIInstrList(typename MIInstrListTraits<T>::ItemParentClass *owner) + { this->setListOwner(owner); } +}; + +class MIInstr final : public llvm::ilist_node_with_parent<MIInstr, MIBlock> +{ + Q_DISABLE_COPY_MOVE(MIInstr) // heap use only! + +protected: + friend class QQmlJS::MemoryPool; + MIInstr() : m_operands(nullptr, 0) {} + + explicit MIInstr(Node *irNode, QQmlJS::MemoryPool *pool, unsigned nOperands) + : m_irNode(irNode) + , m_operands(pool, nOperands) + {} + + ~MIInstr() = default; + +public: + static MIInstr *create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands); + + MIBlock *parent() const + { return m_parent; } + + MIBlock *getParent() const // for ilist_node_with_parent + { return parent(); } + + void setParent(MIBlock *parent) + { m_parent = parent; } + + Node *irNode() const + { return m_irNode; } + + Operation::Kind opcode() const + { return m_irNode->opcode(); } + + int position() const + { return m_position; } + + inline void insertBefore(MIInstr *insertPos); + inline void insertAfter(MIInstr *insertPos); + inline MIInstrList<MIInstr>::iterator eraseFromParent(); + + bool hasDestination() const + { return m_destination.isValid(); } + + MIOperand destination() const + { return m_destination; } + + void setDestination(const MIOperand &dest) + { m_destination = dest; } + + const MIOperand &operand(unsigned index) const + { return m_operands.at(index); } + + void setOperand(unsigned index, const MIOperand &op) + { m_operands.at(index) = op; } + + MIOperand &operand(unsigned index) + { return m_operands.at(index); } + + const MIOperand::List &operands() const + { return m_operands; } + + MIOperand::List &operands() + { return m_operands; } + +private: + friend MIFunction; + void setPosition(int newPosition) + { m_position = newPosition; } + +private: + MIBlock *m_parent = nullptr; + Node *m_irNode = nullptr; + int m_position = -1; + MIOperand m_destination; + MIOperand::List m_operands; +}; + +class MIBlock final +{ + Q_DISABLE_COPY_MOVE(MIBlock) + +public: + using Index = unsigned; + enum : Index { InvalidIndex = std::numeric_limits<Index>::max() }; + + using MIInstructionList = MIInstrList<MIInstr>; + + using InEdges = QVarLengthArray<MIBlock *, 4>; + using OutEdges = QVarLengthArray<MIBlock *, 2>; + +protected: + friend MIFunction; + explicit MIBlock(Index index) + : m_instructions(this), + m_index(index) + {} + + void setIndex(Index newIndex) + { m_index = newIndex; } + +public: + ~MIBlock() = default; + + const MIInstructionList &instructions() const + { return m_instructions; } + + MIInstructionList &instructions() + { return m_instructions; } + + static MIInstructionList MIBlock::*getSublistAccess(MIInstr * = nullptr) + { return &MIBlock::m_instructions; } + + void addArgument(MIOperand &&arg) + { m_arguments.push_back(arg); } + + const std::vector<MIOperand> &arguments() const + { return m_arguments; } + + std::vector<MIOperand> &arguments() + { return m_arguments; } + + void clearArguments() + { m_arguments.resize(0); } + + const InEdges &inEdges() const + { return m_inEdges; } + + void addInEdge(MIBlock *edge) + { m_inEdges.append(edge); } + + const OutEdges &outEdges() const + { return m_outEdges; } + + void addOutEdge(MIBlock *edge) + { m_outEdges.append(edge); } + + Index index() const + { return m_index; } + + MIBlock *findEdgeTo(Operation::Kind target) const; + + bool isDeoptBlock() const + { return m_isDeoptBlock; } + + void markAsDeoptBlock() + { m_isDeoptBlock = true; } + +private: + std::vector<MIOperand> m_arguments; + MIInstructionList m_instructions; + InEdges m_inEdges; + OutEdges m_outEdges; + Index m_index; + bool m_isDeoptBlock = false; +}; + +class MIFunction final +{ + Q_DISABLE_COPY_MOVE(MIFunction) + +public: + static constexpr MIBlock::Index StartBlockIndex = 0; + +public: + MIFunction(Function *irFunction); + ~MIFunction() + { qDeleteAll(m_blocks); } + + Function *irFunction() const + { return m_irFunction; } + + void setStartBlock(MIBlock *newStartBlock); + void renumberBlocks(); + void renumberInstructions(); + + void dump(const QString &description) const; + + size_t blockCount() const + { return blocks().size(); } + + MIBlock *block(MIBlock::Index index) const + { return m_blocks[index]; } + + const std::vector<MIBlock *> &blocks() const + { return m_blocks; } + + MIBlock *addBlock() + { + auto *b = new MIBlock(unsigned(m_blocks.size())); + m_blocks.push_back(b); + return b; + } + + void setBlockOrder(const std::vector<MIBlock *> &newSequence) + { m_blocks = newSequence; } + + unsigned vregCount() const + { return m_vregCount; } + + void setVregCount(unsigned vregCount) + { m_vregCount = vregCount; } + + unsigned dwordSlotCount() const + { return m_dwordSlotCount; } + + unsigned qwordSlotCount() const + { return m_qwordSlotCount; } + + unsigned jsSlotCount() const + { return m_jsSlotCount; } + + unsigned extraJSSlots() const; + + void setStackSlotCounts(unsigned dword, unsigned qword, unsigned js); + + void verifyCFG() const; + +private: + Function *m_irFunction = nullptr; + std::vector<MIBlock *> m_blocks; + unsigned m_vregCount = 0; + unsigned m_dwordSlotCount = 0; + unsigned m_qwordSlotCount = 0; + unsigned m_jsSlotCount = 0; +}; + +Type MIOperand::nodeType(MIFunction *f) const +{ + return f->irFunction()->nodeInfo(irNode())->type(); +} + +inline uint qHash(const MIOperand &key, uint seed) +{ + uint h = ::qHash(key.kind(), seed); + switch (key.kind()) { + case MIOperand::VirtualRegister: + h ^= key.virtualRegister(); + break; + case MIOperand::BoolStackSlot: Q_FALLTHROUGH(); + case MIOperand::JSStackSlot: + h ^= key.stackSlot(); + break; + default: + qFatal("%s: cannot hash %s", Q_FUNC_INFO, key.debugString().toUtf8().constData()); + } + return h; +} + +void MIInstr::insertBefore(MIInstr *insertPos) +{ + insertPos->parent()->instructions().insert(insertPos->getIterator(), this); +} + +void MIInstr::insertAfter(MIInstr *insertPos) +{ + insertPos->parent()->instructions().insert(++insertPos->getIterator(), this); +} + +MIInstrList<MIInstr>::iterator MIInstr::eraseFromParent() +{ + auto p = parent(); + setParent(nullptr); + return p->instructions().erase(getIterator()); +} + +} // namespace IR +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4MI_P_H |