Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

424 lines
13 KiB
C++

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file tvm/ir/op.h
* \brief Primitive operators(builtin intrinsics)
* and registry for them.
*/
#ifndef TVM_IR_OP_H_
#define TVM_IR_OP_H_
#include <tvm/ffi/error.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/attr_registry_map.h>
#include <tvm/ir/attrs.h>
#include <tvm/ir/env_func.h>
#include <tvm/ir/expr.h>
#include <tvm/ir/type.h>
#include <string>
#include <utility>
#include <vector>
namespace tvm {
// forward declare name.
template <typename>
class OpAttrMap;
/*!
* \brief Information about an input field of an Op (name, type, description).
*
* Populated via OpRegEntry::add_argument and consumed both by
* internal sanity checks / error messages and by external tooling
* that wants to introspect an Op's argument schema.
*/
class ArgumentInfoNode : public ffi::Object {
public:
/*! \brief name of the field */
ffi::String name;
/*! \brief type docstring information in str. */
ffi::String type_info;
/*! \brief detailed description of the type */
ffi::String description;
static void RegisterReflection() {
namespace rfl = ffi::reflection;
rfl::ObjectDef<ArgumentInfoNode>()
.def_ro("name", &ArgumentInfoNode::name)
.def_ro("type_info", &ArgumentInfoNode::type_info)
.def_ro("description", &ArgumentInfoNode::description);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.ArgumentInfo", ArgumentInfoNode, ffi::Object);
};
/*! \brief Managed reference to ArgumentInfoNode. */
class ArgumentInfo : public ffi::ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(ArgumentInfo, ffi::ObjectRef, ArgumentInfoNode);
};
// TODO(tvm-team): migrate low-level intrinsics to use Op
/*!
* \brief Primitive Op(builtin intrinsics)
*
* This data structure stores the meta-data
* about primitive operators that can be invoked via Call.
*
* Low-level IR intrinsics(such as libc.expf) are also
* implemented via Op.
*
* \sa Op
*/
class OpNode : public ExprNode {
public:
/*! \brief name of the operator */
ffi::String name;
/*!
* \brief detailed description of the operator
* This can be used to generate docstring automatically for the operator.
*/
ffi::String description;
/* \brief Information of input arguments to the operator */
ffi::Array<ArgumentInfo> arguments;
/*!
* \brief The type key of the attribute field
* This can be empty, in which case it defaults to anything.
*/
ffi::String attrs_type_key;
/*!
* \brief attribute type index,
* this field varies in each run and is not exposed to frontend.
*/
uint32_t attrs_type_index{0};
/*!
* \brief number of input arguments to the operator,
* -1 means it is variable length
*/
int32_t num_inputs = -1;
/*!
* \brief support level of the operator,
* The lower the more priority it contains.
* This is in analogies to BLAS levels.
*/
int32_t support_level = 10;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<OpNode>()
.def_ro("name", &OpNode::name)
.def_ro("description", &OpNode::description, refl::AttachFieldFlag::SEqHashIgnore())
.def_ro("arguments", &OpNode::arguments, refl::AttachFieldFlag::SEqHashIgnore())
.def_ro("attrs_type_key", &OpNode::attrs_type_key, refl::AttachFieldFlag::SEqHashIgnore())
.def_ro("num_inputs", &OpNode::num_inputs, refl::AttachFieldFlag::SEqHashIgnore())
.def_ro("support_level", &OpNode::support_level, refl::AttachFieldFlag::SEqHashIgnore());
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindUniqueInstance;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.Op", OpNode, ExprNode);
private:
/*! \return the internal attr registry index. */
uint32_t AttrRegistryIndex() const { return index_; }
/*! \brief repr to be printed in registry*/
std::string AttrRegistryName() const { return name; }
// friend class
template <typename>
friend class AttrRegistryMapContainerMap;
template <typename, typename>
friend class AttrRegistry;
friend class OpRegEntry;
// Program internal unique index of operator.
// Used to help index the program.
uint32_t index_{0};
};
/*!
* \brief Managed reference class to OpNode.
* \sa OpNode
*/
class Op : public Expr {
public:
explicit Op(ffi::ObjectPtr<OpNode> n) : Expr(std::move(n)) {
TVM_FFI_CHECK(defined(), ValueError) << "Op expects a defined OpNode";
}
/*!
* \brief Get additional registered attribute about operators.
* If nothing has been registered, an empty OpAttrMap will be returned.
* \param attr_name The name of the attribute.
* \return An OpAttrMap of specified attr_name.
* \tparam ValueType The type of the attribute.
*/
template <typename ValueType>
inline static OpAttrMap<ValueType> GetAttrMap(const ffi::String& attr_name);
/*!
* \brief Checks if an attr map is present in the registry.
* \param attr_name The name of the attribute.
* \return bool True if the attr is present.
*/
TVM_DLL static bool HasAttrMap(const ffi::String& attr_name);
/*!
* \brief Get an Op for a given operator name.
* Will raise an error if the op has not been registered.
* \param op_name Name of the operator.
* \return Pointer to a Op, valid throughout program lifetime.
*/
TVM_DLL static const Op& Get(const ffi::String& op_name);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Op, Expr, OpNode);
private:
/*!
* \brief Get generic attrmap given attr name
* \param key The attribute key
* \return The attr map.
*/
TVM_DLL static const AttrRegistryMapContainerMap<Op>& GetAttrMapContainer(const ffi::String& key);
};
/*!
* \brief Helper structure to register operators
* \sa TVM_REGISTER_OP
*/
class OpRegEntry {
public:
/*! \return the operator */
const Op& op() const { return op_; }
/*!
* \brief setter function during registration
* Set the description of operator
* \param descr the description string.
* \return reference to self.
*/
inline OpRegEntry& describe(const std::string& descr); // NOLINT(*)
/*!
* \brief Add argument information to the function.
* \param name Name of the argument.
* \param type Type of the argument.
* \param description Description of the argument.
* \return reference to self.
*/
inline OpRegEntry& add_argument(const std::string& name, const std::string& type,
const std::string& description);
/*!
* \brief Set the attrs type key and index to be AttrsType.
* \tparam AttrsType the attribute type to b set.
* \return reference to self.
*/
template <typename AttrsType>
inline OpRegEntry& set_attrs_type();
/*!
* \brief Set the attrs type key and index to be AttrsType.
* \param key The attribute type key to be set.
* \return reference to self.
*/
inline OpRegEntry& set_attrs_type_key(const ffi::String& key);
/*!
* \brief Set the num_inputs
* \param n The number of inputs to be set.
* \return reference to self.
*/
inline OpRegEntry& set_num_inputs(int32_t n); // NOLINT(*)
/*!
* \brief Set the support level of op.
* \param level The support level.
* \return reference to self.
*/
inline OpRegEntry& set_support_level(int32_t level); // NOLINT(*)
/*!
* \brief Register additional attributes to operator.
* \param attr_name The name of the attribute.
* \param value The value to be set.
* \param plevel The priority level of this set,
* an higher priority level attribute
* will replace lower priority level attribute.
* Must be bigger than 0.
*
* Cannot set with same plevel twice in the code.
*
* \tparam ValueType The type of the value to be set.
*/
template <typename ValueType>
inline OpRegEntry& set_attr(const std::string& attr_name, // NOLINT(*)
const ValueType& value, int plevel = 10);
/*!
* \brief Resets an attr of the registry.
* \param attr_name The name of the attribute.
*/
inline void reset_attr(const std::string& attr_name);
// set the name of the op to be the same as registry
inline OpRegEntry& set_name() { // NOLINT(*)
if (get()->name.length() == 0) {
get()->name = name;
}
return *this;
}
/*!
* \brief Register or get a new entry.
* \param name The name of the operator.
* \return the corresponding entry.
*/
TVM_DLL static OpRegEntry& RegisterOrGet(const ffi::String& name);
private:
template <typename, typename>
friend class AttrRegistry;
// the name
std::string name;
/*! \brief The operator */
Op op_;
/*! \brief Construct the non-null Op for this registry entry. */
static Op MakeOp(uint32_t reg_index);
// private constructor
TVM_DLL OpRegEntry(uint32_t reg_index);
// return internal pointer to op.
inline OpNode* get();
// update the attribute OpAttrMap
TVM_DLL void UpdateAttr(const ffi::String& key, ffi::Any value, int plevel);
};
/*!
* \brief ffi::Map<Op,ValueType> used to store meta-information about Op.
* \tparam ValueType The type of the value stored in map.
*/
template <typename ValueType>
class OpAttrMap : public AttrRegistryMap<Op, ValueType> {
public:
/*!
* \brief get the corresponding value element at op with default value.
* \param expr The key to the map
* \param def_value The default value when the key does not exist
* or if expr is not an Op.
* \return the const reference to the content value.
*/
inline ValueType get(const Expr& expr, ValueType def_value) const;
using TParent = AttrRegistryMap<Op, ValueType>;
using TParent::count;
using TParent::get;
using TParent::operator[];
private:
friend class Op;
// constructor
explicit OpAttrMap(const AttrRegistryMapContainerMap<Op>& map) : TParent(map) {}
};
// internal macros to make
#define TVM_OP_REGISTER_VAR_DEF [[maybe_unused]] static ::tvm::OpRegEntry& __make_##Op
/*!
* \def TVM_REGISTER_OP
* \brief Register a new operator, or set attribute of the corresponding op.
*
* \param OpName The name of registry
*
* \code
*
* TVM_REGISTER_OP("add")
* .describe("add two inputs together")
* .set_num_inputs(2)
* .set_attr<OpKernel>("gpu_kernel", AddKernel);
*
* \endcode
*/
#define TVM_REGISTER_OP(OpName) \
TVM_FFI_STR_CONCAT(TVM_OP_REGISTER_VAR_DEF, __COUNTER__) = \
::tvm::OpRegEntry::RegisterOrGet(OpName).set_name()
// implementations
template <typename ValueType>
inline OpAttrMap<ValueType> Op::GetAttrMap(const ffi::String& key) {
return OpAttrMap<ValueType>(Op::GetAttrMapContainer(key));
}
inline OpNode* OpRegEntry::get() { return const_cast<OpNode*>(op_.operator->()); }
inline OpRegEntry& OpRegEntry::describe(const std::string& descr) { // NOLINT(*)
get()->description = descr;
return *this;
}
inline OpRegEntry& OpRegEntry::add_argument(const std::string& name, const std::string& type,
const std::string& description) {
auto n = ffi::make_object<ArgumentInfoNode>();
n->name = name;
n->type_info = type;
n->description = description;
get()->arguments.push_back(ArgumentInfo(n));
return *this;
}
inline OpRegEntry& OpRegEntry::set_num_inputs(int32_t n) { // NOLINT(*)
get()->num_inputs = n;
return *this;
}
template <typename AttrsType>
inline OpRegEntry& OpRegEntry::set_attrs_type() { // NOLINT(*)
get()->attrs_type_key = AttrsType::_type_key;
get()->attrs_type_index = AttrsType::RuntimeTypeIndex();
return *this;
}
inline OpRegEntry& OpRegEntry::set_attrs_type_key(const ffi::String& key) { // NOLINT(*)
get()->attrs_type_key = key;
get()->attrs_type_index = tvm::ffi::TypeKeyToIndex(key.c_str());
return *this;
}
inline OpRegEntry& OpRegEntry::set_support_level(int32_t n) { // NOLINT(*)
get()->support_level = n;
return *this;
}
template <typename ValueType>
inline OpRegEntry& OpRegEntry::set_attr( // NOLINT(*)
const std::string& attr_name, const ValueType& value, int plevel) {
TVM_FFI_ICHECK_GT(plevel, 0) << "plevel in set_attr must be greater than 0";
UpdateAttr(attr_name, Any(value), plevel);
return *this;
}
// member functions of OpAttrMap
template <typename ValueType>
inline ValueType OpAttrMap<ValueType>::get(const Expr& expr, ValueType def_value) const {
TVM_FFI_ICHECK(expr.defined());
if (const OpNode* op = expr.as<OpNode>()) {
return this->map_.get(ffi::GetRef<Op>(op), def_value);
} else {
return def_value;
}
}
} // namespace tvm
#endif // TVM_IR_OP_H_