Program Listing for File attrs.h#
↰ Return to documentation for file (tvm/ir/attrs.h)
/*
* 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.
*/
#ifndef TVM_IR_ATTRS_H_
#define TVM_IR_ATTRS_H_
#include <tvm/ffi/container/map.h>
#include <tvm/ffi/extra/structural_equal.h>
#include <tvm/ffi/extra/structural_hash.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/accessor.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/cow.h>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
namespace tvm {
class AttrsObj : public ffi::Object {
public:
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO("ir.Attrs", AttrsObj, ffi::Object);
};
class Attrs : public ffi::ObjectRef {
public:
explicit Attrs(ffi::ObjectPtr<AttrsObj> n) : ffi::ObjectRef(ffi::UnsafeInit{}) {
TVM_FFI_ICHECK(n != nullptr);
data_ = std::move(n);
}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Attrs, ffi::ObjectRef, AttrsObj);
};
class DictAttrsObj : public AttrsObj {
public:
ffi::Map<ffi::String, ffi::Any> dict;
static void RegisterReflection() {
namespace rfl = ffi::reflection;
rfl::ObjectDef<DictAttrsObj>().def_ro("dict", &DictAttrsObj::dict);
}
// type info
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.DictAttrs", DictAttrsObj, AttrsObj);
};
class DictAttrs : public Attrs {
public:
explicit DictAttrs(ffi::Map<ffi::String, Any> dict = {}) : Attrs(ffi::UnsafeInit{}) {
ffi::ObjectPtr<DictAttrsObj> n = ffi::make_object<DictAttrsObj>();
n->dict = std::move(dict);
data_ = std::move(n);
}
DictAttrs(DictAttrs&& other) noexcept : Attrs(ffi::UnsafeInit{}) {
data_ = std::move(other.data_);
other.data_ = ffi::make_object<DictAttrsObj>();
}
DictAttrs& operator=(DictAttrs&& other) noexcept {
if (this != &other) {
data_ = std::move(other.data_);
other.data_ = ffi::make_object<DictAttrsObj>();
}
return *this;
}
// Explicit copy ctor/assign defaults. Declaring the move members above
// would otherwise suppress the implicit copy members.
DictAttrs(const DictAttrs& other) = default;
DictAttrs& operator=(const DictAttrs& other) = default;
// Utils for accessing attributes
template <typename TObjectRef>
ffi::Optional<TObjectRef> GetAttr(
const std::string& attr_key,
ffi::Optional<TObjectRef> default_value = ffi::Optional<TObjectRef>(std::nullopt)) const {
const DictAttrsObj* node = get();
auto it = node->dict.find(attr_key);
if (it != node->dict.end()) {
return (*it).second.cast<TObjectRef>();
} else {
return default_value;
}
}
template <typename T>
ffi::Optional<T> GetAttr(ffi::reflection::AttrKey<T> key,
ffi::Optional<typename ffi::reflection::AttrKey<T>::ValueType>
default_value = std::nullopt) const {
return GetAttr<T>(key.name, std::move(default_value));
}
template <typename T>
ffi::Optional<T> GetAttr(ffi::reflection::AttrKey<T> key,
typename ffi::reflection::AttrKey<T>::ValueType default_value) const {
return GetAttr(key, ffi::Optional<T>(std::move(default_value)));
}
// variant that uses TObjectRef to enable implicit conversion to default value.
template <typename TObjectRef>
ffi::Optional<TObjectRef> GetAttr(const std::string& attr_key, TObjectRef default_value) const {
return GetAttr<TObjectRef>(attr_key, ffi::Optional<TObjectRef>(default_value));
}
bool HasNonzeroAttr(const std::string& attr_key) const {
return GetAttr<int64_t>(attr_key, 0).value_or(0) != 0;
}
// Inline-expand TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE here, minus
// the default copy/move it normally injects (we define our own move members
// above so the moved-from instance stays defined-but-empty).
explicit DictAttrs(::tvm::ffi::UnsafeInit tag) : Attrs(tag) {}
using __PtrType =
std::conditional_t<DictAttrsObj::_type_mutable, DictAttrsObj*, const DictAttrsObj*>;
__PtrType operator->() const { return static_cast<__PtrType>(data_.get()); }
__PtrType get() const { return static_cast<__PtrType>(data_.get()); }
static constexpr bool _type_is_nullable = false;
using ContainerType = DictAttrsObj;
TVM_DEFINE_OBJECT_REF_COW_METHOD(DictAttrsObj);
};
template <typename TFunc>
inline TFunc WithAttr(TFunc input, const std::string& attr_key, Any attr_value) {
using TNode = typename TFunc::ContainerType;
if (!input.unique()) {
static ffi::reflection::TypeAttrColumn shallow_copy(ffi::type_attr::kShallowCopy);
ffi::AnyView copy_func = shallow_copy[input->type_index()];
TVM_FFI_CHECK(copy_func.type_index() == ffi::TypeIndex::kTVMFFIFunction, TypeError)
<< "Type " << input->GetTypeKey()
<< " must register an ffi.Function for __ffi_shallow_copy__";
Any copy = copy_func.cast<ffi::Function>()(input);
TVM_FFI_CHECK(copy.type_index() == input->type_index() && copy.as<ffi::Object>() != input.get(),
TypeError)
<< "Shallow copy must return a distinct object of type " << input->GetTypeKey();
input = std::move(copy).cast<TFunc>();
}
TNode* node = const_cast<TNode*>(input.operator->());
// node->attrs is NOTNULLABLE by contract, but defend against a caller
// that left a moved-from DictAttrs in place by re-initializing here.
if (!node->attrs.defined()) node->attrs = DictAttrs();
node->attrs.CopyOnWrite()->dict.Set(attr_key, std::move(attr_value));
return input;
}
template <typename TFunc, typename T, typename Value,
std::enable_if_t<(std::is_convertible_v<Value&&, T> &&
((!std::is_same_v<std::decay_t<Value>, ffi::Any> &&
!std::is_same_v<std::decay_t<Value>, ffi::AnyView>) ||
std::is_same_v<T, ffi::Any> || std::is_same_v<T, ffi::AnyView>)),
int> = 0>
inline TFunc WithAttr(TFunc input, ffi::reflection::AttrKey<T> key, Value&& value) {
T typed_value(std::forward<Value>(value));
return WithAttr(std::move(input), key.name, ffi::Any(typed_value));
}
template <typename TFunc>
inline TFunc WithAttrs(TFunc input, ffi::Map<ffi::String, Any> attrs) {
using TNode = typename TFunc::ContainerType;
if (attrs.empty()) return input;
if (!input.unique()) {
static ffi::reflection::TypeAttrColumn shallow_copy(ffi::type_attr::kShallowCopy);
ffi::AnyView copy_func = shallow_copy[input->type_index()];
TVM_FFI_CHECK(copy_func.type_index() == ffi::TypeIndex::kTVMFFIFunction, TypeError)
<< "Type " << input->GetTypeKey()
<< " must register an ffi.Function for __ffi_shallow_copy__";
Any copy = copy_func.cast<ffi::Function>()(input);
TVM_FFI_CHECK(copy.type_index() == input->type_index() && copy.as<ffi::Object>() != input.get(),
TypeError)
<< "Shallow copy must return a distinct object of type " << input->GetTypeKey();
input = std::move(copy).cast<TFunc>();
}
TNode* node = const_cast<TNode*>(input.operator->());
// node->attrs is NOTNULLABLE by contract, but defend against a caller
// that left a moved-from DictAttrs in place by re-initializing here.
if (!node->attrs.defined()) node->attrs = DictAttrs();
auto* dict_node = node->attrs.CopyOnWrite();
for (const auto& [k, v] : attrs) {
dict_node->dict.Set(k, v);
}
return input;
}
template <typename TFunc>
inline TFunc WithoutAttr(TFunc input, const std::string& attr_key) {
using TNode = typename TFunc::ContainerType;
if (!input.unique()) {
static ffi::reflection::TypeAttrColumn shallow_copy(ffi::type_attr::kShallowCopy);
ffi::AnyView copy_func = shallow_copy[input->type_index()];
TVM_FFI_CHECK(copy_func.type_index() == ffi::TypeIndex::kTVMFFIFunction, TypeError)
<< "Type " << input->GetTypeKey()
<< " must register an ffi.Function for __ffi_shallow_copy__";
Any copy = copy_func.cast<ffi::Function>()(input);
TVM_FFI_CHECK(copy.type_index() == input->type_index() && copy.as<ffi::Object>() != input.get(),
TypeError)
<< "Shallow copy must return a distinct object of type " << input->GetTypeKey();
input = std::move(copy).cast<TFunc>();
}
TNode* node = const_cast<TNode*>(input.operator->());
// node->attrs is NOTNULLABLE by contract, but defend against a caller
// that left a moved-from DictAttrs in place; nothing to erase from an
// empty dict.
if (!node->attrs.defined()) {
node->attrs = DictAttrs();
return input;
}
node->attrs.CopyOnWrite()->dict.erase(attr_key);
return input;
}
template <typename TFunc, typename T>
inline TFunc WithoutAttr(TFunc input, ffi::reflection::AttrKey<T> key) {
return WithoutAttr(std::move(input), key.name);
}
} // namespace tvm
#endif // TVM_IR_ATTRS_H_