#pragma once #include #include #include #include #include #include "attribute.h" #include "util/iterators.h" #include "util/visitor.h" #include "util/common.h" #include "util/fmt/format.h" #include "util/fmt/ostream.h" namespace seq { namespace ir { using id_t = std::int64_t; class Func; class Module; /// Mixin class for IR nodes that need ids. class IdMixin { private: /// the global id counter static id_t currentId; protected: /// the instance's id id_t id; public: /// Resets the global id counter. static void resetId(); IdMixin() : id(currentId++) {} /// @return the node's id. virtual id_t getId() const { return id; } }; /// Base for named IR nodes. class Node { private: /// the node's name std::string name; /// key-value attribute store std::map> attributes; /// the module Module *module = nullptr; /// a replacement, if set Node *replacement = nullptr; public: // RTTI is implemented using a port of LLVM's Extensible RTTI // For more details, see // https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html#rtti-for-open-class-hierarchies static const char NodeId; /// Constructs a node. /// @param name the node's name explicit Node(std::string name = "") : name(std::move(name)) {} /// See LLVM documentation. static const void *nodeId() { return &NodeId; } /// See LLVM documentation. virtual bool isConvertible(const void *other) const { if (hasReplacement()) return getActual()->isConvertible(other); return other == nodeId(); } /// See LLVM documentation. template bool is() const { return isConvertible(Target::nodeId()); } /// See LLVM documentation. template Target *as() { return isConvertible(Target::nodeId()) ? static_cast(getActual()) : nullptr; } /// See LLVM documentation. template const Target *as() const { return isConvertible(Target::nodeId()) ? static_cast(getActual()) : nullptr; } /// @return the node's name const std::string &getName() const { return getActual()->name; } /// Sets the node's name /// @param n the new name void setName(std::string n) { getActual()->name = std::move(n); } /// Accepts visitors. /// @param v the visitor virtual void accept(util::Visitor &v) = 0; /// Accepts visitors. /// @param v the visitor virtual void accept(util::ConstVisitor &v) const = 0; /// Sets an attribute /// @param the attribute key /// @param value the attribute void setAttribute(std::unique_ptr value, const std::string &key) { getActual()->attributes[key] = std::move(value); } /// Sets an attribute /// @param value the attribute template void setAttribute(std::unique_ptr value) { setAttribute(std::move(value), AttributeType::AttributeName); } /// @param n the name /// @return true if the attribute is in the store bool hasAttribute(const std::string &n) const { auto *actual = getActual(); return actual->attributes.find(n) != actual->attributes.end(); } /// @return true if the attribute is in the store template bool hasAttribute() const { return hasAttribute(AttributeType::AttributeName); } /// Gets the appropriate attribute. /// @param key the attribute key Attribute *getAttribute(const std::string &key) { auto *actual = getActual(); auto it = actual->attributes.find(key); return it != actual->attributes.end() ? it->second.get() : nullptr; } /// Gets the appropriate attribute. /// @param key the attribute key const Attribute *getAttribute(const std::string &key) const { auto *actual = getActual(); auto it = actual->attributes.find(key); return it != actual->attributes.end() ? it->second.get() : nullptr; } /// Gets the appropriate attribute. /// @tparam AttributeType the return type template AttributeType *getAttribute() { return static_cast(getAttribute(AttributeType::AttributeName)); } /// Gets the appropriate attribute. /// @tparam AttributeType the return type template const AttributeType *getAttribute() const { return static_cast( getAttribute(AttributeType::AttributeName)); } /// @return iterator to the first attribute auto attributes_begin() { return util::map_key_adaptor(getActual()->attributes.begin()); } /// @return iterator beyond the last attribute auto attributes_end() { return util::map_key_adaptor(getActual()->attributes.end()); } /// @return iterator to the first attribute auto attributes_begin() const { return util::const_map_key_adaptor(getActual()->attributes.begin()); } /// @return iterator beyond the last attribute auto attributes_end() const { return util::const_map_key_adaptor(getActual()->attributes.end()); } /// Helper to add source information. /// @param the source information void setSrcInfo(seq::SrcInfo s) { setAttribute(std::make_unique(std::move(s))); } /// @return the src info seq::SrcInfo getSrcInfo() const { return getAttribute() ? getAttribute()->info : seq::SrcInfo(); } /// @return a text representation of a reference to the object virtual std::string referenceString() const { return getActual()->name; } /// @return the IR module Module *getModule() const { return getActual()->module; } /// Sets the module. /// @param m the new module void setModule(Module *m) { getActual()->module = m; } friend std::ostream &operator<<(std::ostream &os, const Node &a); bool hasReplacement() const { return replacement != nullptr; } /// @return a vector of all the node's children virtual std::vector getUsedValues() { return {}; } /// @return a vector of all the node's children virtual std::vector getUsedValues() const { return {}; } /// Physically replaces all instances of a child value. /// @param id the id of the value to be replaced /// @param newValue the new value /// @return number of replacements virtual int replaceUsedValue(id_t id, Value *newValue) { return 0; } /// Physically replaces all instances of a child value. /// @param oldValue the old value /// @param newValue the new value /// @return number of replacements int replaceUsedValue(Value *old, Value *newValue); /// @return a vector of all the node's used types virtual std::vector getUsedTypes() const { return {}; } /// Physically replaces all instances of a used type. /// @param name the name of the type being replaced /// @param newType the new type /// @return number of replacements virtual int replaceUsedType(const std::string &name, types::Type *newType) { return 0; } /// Physically replaces all instances of a used type. /// @param old the old type /// @param newType the new type /// @return number of replacements int replaceUsedType(types::Type *old, types::Type *newType); /// @return a vector of all the node's used variables virtual std::vector getUsedVariables() { return {}; } /// @return a vector of all the node's used variables virtual std::vector getUsedVariables() const { return {}; } /// Physically replaces all instances of a used variable. /// @param id the id of the variable /// @param newType the new type /// @return number of replacements virtual int replaceUsedVariable(id_t id, Var *newVar) { return 0; } /// Physically replaces all instances of a used variable. /// @param old the old variable /// @param newVar the new variable /// @return number of replacements int replaceUsedVariable(Var *old, Var *newVar); template friend class AcceptorExtend; template friend class ReplaceableNodeBase; private: Node *getActual() { return replacement ? replacement->getActual() : this; } const Node *getActual() const { return replacement ? replacement->getActual() : this; } }; template class AcceptorExtend : public Parent { public: using Parent::Parent; /// See LLVM documentation. static const void *nodeId() { return &Derived::NodeId; } /// See LLVM documentation. virtual bool isConvertible(const void *other) const { if (Node::hasReplacement()) return Node::getActual()->isConvertible(other); return other == nodeId() || Parent::isConvertible(other); } void accept(util::Visitor &v) { if (Node::hasReplacement()) Node::getActual()->accept(v); else v.visit(static_cast(this)); } void accept(util::ConstVisitor &v) const { if (Node::hasReplacement()) Node::getActual()->accept(v); else v.visit(static_cast(this)); } }; template class ReplaceableNodeBase : public AcceptorExtend { private: /// true if the node can be lazily replaced bool replaceable = true; public: using AcceptorExtend::AcceptorExtend; static const char NodeId; /// @return the logical value of the node Derived *getActual() { return Node::replacement ? static_cast(Node::replacement)->getActual() : static_cast(this); } /// @return the logical value of the node const Derived *getActual() const { return Node::replacement ? static_cast(Node::replacement)->getActual() : static_cast(this); } /// Lazily replaces all instances of the node. /// @param v the new value void replaceAll(Derived *v) { seqassert(replaceable, "node {} not replaceable", *v); Node::replacement = v; } /// @return true if the object can be replaced bool isReplaceable() const { return replaceable; } /// Sets the object's replaceable flag. /// @param v the new value void setReplaceable(bool v = true) { replaceable = v; } }; template const char ReplaceableNodeBase::NodeId = 0; template Desired *cast(Node *other) { return other != nullptr ? other->as() : nullptr; } template const Desired *cast(const Node *other) { return other != nullptr ? other->as() : nullptr; } template bool isA(Node *other) { return other && other->is(); } template bool isA(const Node *other) { return other && other->is(); } } // namespace ir } // namespace seq // See https://github.com/fmtlib/fmt/issues/1283. namespace fmt { using seq::ir::Node; template struct formatter : fmt::v6::internal::fallback_formatter {}; } // namespace fmt