Skip to content

Commit f0bf057

Browse files
authored
[FFI][REFACTOR] Migrate StructuralEqual/Hash to new reflection (#18166)
This PR migrates the StructuralEqual/Hash to new reflection based approach. The original mechanisms are still kept around and we will phase them out in followup PRs. The new mechanism unifies the structural equal/hash registration with the normal reflection registeration and also brings cleaner implementation for mismatch detection.
1 parent 20fb706 commit f0bf057

40 files changed

Lines changed: 462 additions & 164 deletions

ffi/include/tvm/ffi/c_api.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -424,27 +424,6 @@ typedef enum {
424424
* is only an unique copy of each value.
425425
*/
426426
kTVMFFISEqHashKindUniqueInstance = 5,
427-
/*!
428-
* \brief provide custom __s_equal__ and __s_hash__ functions through TypeAttrColumn.
429-
*
430-
* The function signatures are(defined via ffi::Function)
431-
*
432-
* \code
433-
* bool __s_equal__(
434-
* ObjectRefType self, ObjectRefType other,
435-
* ffi::TypedFunction<bool(AnyView, AnyView, bool def_region, string field_name)> cmp,
436-
* );
437-
*
438-
* uint64_t __s_hash__(
439-
* ObjectRefType self, uint64_t type_key_hash,
440-
* ffi::TypedFunction<uint64_t(AnyView, bool def_region)> hash
441-
* );
442-
* \endcode
443-
*
444-
* Where the extra string field in cmp is the name of the field that is being compared.
445-
* The function should be registered through TVMFFITypeRegisterAttr via reflection::TypeAttrDef.
446-
*/
447-
kTVMFFISEqHashKindCustomTreeNode = 6,
448427
#ifdef __cplusplus
449428
};
450429
#else

ffi/src/ffi/reflection/structural_equal.cc

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <tvm/ffi/reflection/structural_equal.h>
3030
#include <tvm/ffi/string.h>
3131

32+
#include <cmath>
3233
#include <unordered_map>
3334

3435
namespace tvm {
@@ -49,7 +50,12 @@ class StructEqualHandler {
4950
if (lhs_data->type_index != rhs_data->type_index) {
5051
return false;
5152
}
53+
5254
if (lhs_data->type_index < TypeIndex::kTVMFFIStaticObjectBegin) {
55+
// specially handle nan for float, as there can be multiple representations of nan
56+
if (lhs_data->type_index == TypeIndex::kTVMFFIFloat && std::isnan(lhs_data->v_float64)) {
57+
return std::isnan(rhs_data->v_float64);
58+
}
5359
// this is POD data, we can just compare the value
5460
return lhs_data->v_int64 == rhs_data->v_int64;
5561
}
@@ -90,12 +96,18 @@ class StructEqualHandler {
9096
// NOTE: invariant: lhs and rhs are already the same type
9197
const TVMFFITypeInfo* type_info = TVMFFIGetTypeInfo(lhs->type_index());
9298
if (type_info->metadata == nullptr) {
93-
return lhs.same_as(rhs);
99+
TVM_FFI_THROW(TypeError) << "Type metadata is not set for type `"
100+
<< String(type_info->type_key)
101+
<< "`, so StructuralHash is not supported for this type";
102+
}
103+
if (type_info->metadata->structural_eq_hash_kind == kTVMFFISEqHashKindUnsupported) {
104+
TVM_FFI_THROW(TypeError) << "_type_s_eq_hash_kind is not set for type `"
105+
<< String(type_info->type_key)
106+
<< "`, so StructuralHash is not supported for this type";
94107
}
95-
auto structural_eq_hash_kind = type_info->metadata->structural_eq_hash_kind;
96108

97-
if (structural_eq_hash_kind == kTVMFFISEqHashKindUnsupported ||
98-
structural_eq_hash_kind == kTVMFFISEqHashKindUniqueInstance) {
109+
auto structural_eq_hash_kind = type_info->metadata->structural_eq_hash_kind;
110+
if (structural_eq_hash_kind == kTVMFFISEqHashKindUniqueInstance) {
99111
// use pointer comparison
100112
return lhs.same_as(rhs);
101113
}
@@ -118,8 +130,10 @@ class StructEqualHandler {
118130
}
119131
}
120132

133+
static reflection::TypeAttrColumn custom_s_equal = reflection::TypeAttrColumn("__s_equal__");
134+
121135
bool success = true;
122-
if (structural_eq_hash_kind != kTVMFFISEqHashKindCustomTreeNode) {
136+
if (custom_s_equal[type_info->type_index] == nullptr) {
123137
// We recursively compare the fields the object
124138
ForEachFieldInfoWithEarlyStop(type_info, [&](const TVMFFIFieldInfo* field_info) {
125139
// skip fields that are marked as structural eq hash ignore
@@ -153,7 +167,6 @@ class StructEqualHandler {
153167
}
154168
});
155169
} else {
156-
static reflection::TypeAttrColumn custom_s_equal = reflection::TypeAttrColumn("__s_equal__");
157170
// run custom equal function defined via __s_equal__ type attribute
158171
if (s_equal_callback_ == nullptr) {
159172
s_equal_callback_ = ffi::Function::FromTyped(
@@ -179,9 +192,6 @@ class StructEqualHandler {
179192
return success;
180193
});
181194
}
182-
TVM_FFI_ICHECK(custom_s_equal[type_info->type_index] != nullptr)
183-
<< "TypeAttr `__s_equal__` is not registered for type `" << String(type_info->type_key)
184-
<< "`";
185195
success = custom_s_equal[type_info->type_index]
186196
.cast<ffi::Function>()(lhs, rhs, s_equal_callback_)
187197
.cast<bool>();

ffi/src/ffi/reflection/structural_hash.cc

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <tvm/ffi/reflection/structural_hash.h>
3131
#include <tvm/ffi/string.h>
3232

33+
#include <cmath>
34+
#include <limits>
3335
#include <unordered_map>
3436
#include <utility>
3537

@@ -48,6 +50,13 @@ class StructuralHashHandler {
4850
const TVMFFIAny* src_data = AnyUnsafe::TVMFFIAnyPtrFromAny(src);
4951

5052
if (src_data->type_index < TypeIndex::kTVMFFIStaticObjectBegin) {
53+
// specially handle nan for float, as there can be multiple representations of nan
54+
// make sure they map to the same hash value
55+
if (src_data->type_index == TypeIndex::kTVMFFIFloat && std::isnan(src_data->v_float64)) {
56+
TVMFFIAny temp = *src_data;
57+
temp.v_float64 = std::numeric_limits<double>::quiet_NaN();
58+
return details::StableHashCombine(temp.type_index, temp.v_uint64);
59+
}
5160
// this is POD data, we can just hash the value
5261
return details::StableHashCombine(src_data->type_index, src_data->v_uint64);
5362
}
@@ -83,9 +92,16 @@ class StructuralHashHandler {
8392
// NOTE: invariant: lhs and rhs are already the same type
8493
const TVMFFITypeInfo* type_info = TVMFFIGetTypeInfo(obj->type_index());
8594
if (type_info->metadata == nullptr) {
86-
// Fallback to pointer hash
87-
return std::hash<const Object*>()(obj.get());
95+
TVM_FFI_THROW(TypeError) << "Type metadata is not set for type `"
96+
<< String(type_info->type_key)
97+
<< "`, so StructuralHash is not supported for this type";
8898
}
99+
if (type_info->metadata->structural_eq_hash_kind == kTVMFFISEqHashKindUnsupported) {
100+
TVM_FFI_THROW(TypeError) << "_type_s_eq_hash_kind is not set for type `"
101+
<< String(type_info->type_key)
102+
<< "`, so StructuralHash is not supported for this type";
103+
}
104+
89105
auto structural_eq_hash_kind = type_info->metadata->structural_eq_hash_kind;
90106
if (structural_eq_hash_kind == kTVMFFISEqHashKindUnsupported) {
91107
// Fallback to pointer hash
@@ -97,9 +113,11 @@ class StructuralHashHandler {
97113
return it->second;
98114
}
99115

116+
static reflection::TypeAttrColumn custom_s_hash = reflection::TypeAttrColumn("__s_hash__");
117+
100118
// compute the hash value
101119
uint64_t hash_value = obj->GetTypeKeyHash();
102-
if (structural_eq_hash_kind != kTVMFFISEqHashKindCustomTreeNode) {
120+
if (custom_s_hash[type_info->type_index] == nullptr) {
103121
// go over the content and hash the fields
104122
ForEachFieldInfo(type_info, [&](const TVMFFIFieldInfo* field_info) {
105123
// skip fields that are marked as structural eq hash ignore
@@ -119,22 +137,19 @@ class StructuralHashHandler {
119137
}
120138
});
121139
} else {
122-
static reflection::TypeAttrColumn custom_s_hash = reflection::TypeAttrColumn("__s_hash__");
123-
TVM_FFI_ICHECK(custom_s_hash[type_info->type_index] != nullptr)
124-
<< "TypeAttr `__s_hash__` is not registered for type `" << String(type_info->type_key)
125-
<< "`";
126140
if (s_hash_callback_ == nullptr) {
127-
s_hash_callback_ = ffi::Function::FromTyped([this](AnyView val, bool def_region) {
128-
if (def_region) {
129-
bool allow_free_var = true;
130-
std::swap(allow_free_var, map_free_vars_);
131-
uint64_t hash_value = HashAny(val);
132-
std::swap(allow_free_var, map_free_vars_);
133-
return hash_value;
134-
} else {
135-
return HashAny(val);
136-
}
137-
});
141+
s_hash_callback_ =
142+
ffi::Function::FromTyped([this](AnyView val, uint64_t init_hash, bool def_region) {
143+
if (def_region) {
144+
bool allow_free_var = true;
145+
std::swap(allow_free_var, map_free_vars_);
146+
uint64_t hash_value = HashAny(val);
147+
std::swap(allow_free_var, map_free_vars_);
148+
return details::StableHashCombine(init_hash, hash_value);
149+
} else {
150+
return details::StableHashCombine(init_hash, HashAny(val));
151+
}
152+
});
138153
}
139154
hash_value = custom_s_hash[type_info->type_index]
140155
.cast<ffi::Function>()(obj, hash_value, s_hash_callback_)

ffi/tests/cpp/testing_object.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,11 @@ class TCustomFuncObj : public Object {
227227
return true;
228228
}
229229

230-
uint64_t SHash(uint64_t type_key_hash, ffi::TypedFunction<uint64_t(AnyView, bool)> hash) const {
231-
uint64_t hash_value = type_key_hash;
232-
hash_value = tvm::ffi::details::StableHashCombine(hash_value, hash(params, true));
233-
hash_value = tvm::ffi::details::StableHashCombine(hash_value, hash(body, false));
230+
uint64_t SHash(uint64_t init_hash,
231+
ffi::TypedFunction<uint64_t(AnyView, uint64_t, bool)> hash) const {
232+
uint64_t hash_value = init_hash;
233+
hash_value = hash(params, hash_value, true);
234+
hash_value = hash(body, hash_value, false);
234235
return hash_value;
235236
}
236237

@@ -246,7 +247,7 @@ class TCustomFuncObj : public Object {
246247
}
247248

248249
static constexpr const char* _type_key = "test.CustomFunc";
249-
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindCustomTreeNode;
250+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
250251
TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TCustomFuncObj, Object);
251252
};
252253

include/tvm/arith/analyzer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class ConstIntBoundNode : public Object {
106106
*/
107107
static const constexpr int64_t kNegInf = -kPosInf;
108108

109+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
109110
static constexpr const char* _type_key = "arith.ConstIntBound";
110111
TVM_DECLARE_FINAL_OBJECT_INFO(ConstIntBoundNode, Object);
111112
};
@@ -222,6 +223,7 @@ class ModularSetNode : public Object {
222223
return equal(coeff, other->coeff) && equal(base, other->base);
223224
}
224225

226+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
225227
static constexpr const char* _type_key = "arith.ModularSet";
226228
TVM_DECLARE_FINAL_OBJECT_INFO(ModularSetNode, Object);
227229
};

include/tvm/arith/int_solver.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class IntGroupBoundsNode : public Object {
8383
hash_reduce(upper);
8484
}
8585

86+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
8687
static constexpr const bool _type_has_method_sequal_reduce = true;
8788
static constexpr const char* _type_key = "arith.IntGroupBounds";
8889
TVM_DECLARE_FINAL_OBJECT_INFO(IntGroupBoundsNode, Object);
@@ -173,6 +174,7 @@ class IntConstraintsNode : public Object {
173174
hash_reduce(relations);
174175
}
175176

177+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
176178
static constexpr const bool _type_has_method_sequal_reduce = true;
177179
static constexpr const char* _type_key = "arith.IntConstraints";
178180
TVM_DECLARE_FINAL_OBJECT_INFO(IntConstraintsNode, Object);
@@ -238,6 +240,7 @@ class IntConstraintsTransformNode : public Object {
238240
hash_reduce(dst_to_src);
239241
}
240242

243+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
241244
static constexpr const bool _type_has_method_sequal_reduce = true;
242245
static constexpr const char* _type_key = "arith.IntConstraintsTransform";
243246
TVM_DECLARE_FINAL_OBJECT_INFO(IntConstraintsTransformNode, Object);

include/tvm/arith/iter_affine_map.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class IterMarkNode : public Object {
116116
hash_reduce(extent);
117117
}
118118

119+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindDAGNode;
119120
static constexpr const bool _type_has_method_sequal_reduce = true;
120121
static constexpr const bool _type_has_method_shash_reduce = true;
121122
static constexpr const char* _type_key = "arith.IterMark";
@@ -176,6 +177,7 @@ class IterSplitExprNode : public IterMapExprNode {
176177
hash_reduce(scale);
177178
}
178179

180+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
179181
static constexpr const char* _type_key = "arith.IterSplitExpr";
180182
TVM_DECLARE_FINAL_OBJECT_INFO(IterSplitExprNode, IterMapExprNode);
181183
};
@@ -239,6 +241,7 @@ class IterSumExprNode : public IterMapExprNode {
239241
hash_reduce(base);
240242
}
241243

244+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
242245
static constexpr const char* _type_key = "arith.IterSumExpr";
243246
TVM_DECLARE_FINAL_OBJECT_INFO(IterSumExprNode, IterMapExprNode);
244247
};

include/tvm/ir/attrs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class AttrFieldInfoNode : public Object {
8383
}
8484

8585
static constexpr const char* _type_key = "ir.AttrFieldInfo";
86-
86+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
8787
static constexpr bool _type_has_method_sequal_reduce = false;
8888
static constexpr bool _type_has_method_shash_reduce = false;
8989
TVM_DECLARE_FINAL_OBJECT_INFO(AttrFieldInfoNode, Object);
@@ -122,6 +122,7 @@ class BaseAttrsNode : public Object {
122122
TVM_DLL virtual void InitByPackedArgs(const ffi::PackedArgs& kwargs,
123123
bool allow_unknown = false) = 0;
124124

125+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
125126
static constexpr const bool _type_has_method_sequal_reduce = true;
126127
static constexpr const bool _type_has_method_shash_reduce = true;
127128
static constexpr const char* _type_key = "ir.Attrs";

include/tvm/ir/diagnostic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class DiagnosticNode : public Object {
7979
equal(this->message, other->message);
8080
}
8181

82+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
8283
static constexpr const char* _type_key = "Diagnostic";
8384
TVM_DECLARE_FINAL_OBJECT_INFO(DiagnosticNode, Object);
8485
};
@@ -214,6 +215,7 @@ class DiagnosticContextNode : public Object {
214215
return equal(module, other->module) && equal(diagnostics, other->diagnostics);
215216
}
216217

218+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
217219
static constexpr const char* _type_key = "DiagnosticContext";
218220
TVM_DECLARE_FINAL_OBJECT_INFO(DiagnosticContextNode, Object);
219221
};

include/tvm/ir/env_func.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ class EnvFuncNode : public Object {
5151

5252
static void RegisterReflection() {
5353
namespace refl = tvm::ffi::reflection;
54-
refl::ObjectDef<EnvFuncNode>().def_ro("name", &EnvFuncNode::name);
54+
// func do not participate in structural equal and hash.
55+
refl::ObjectDef<EnvFuncNode>()
56+
.def_ro("name", &EnvFuncNode::name)
57+
.def_ro("func", &EnvFuncNode::func, refl::AttachFieldFlag::SEqHashIgnore());
5558
}
5659

5760
bool SEqualReduce(const EnvFuncNode* other, SEqualReducer equal) const {
@@ -64,6 +67,7 @@ class EnvFuncNode : public Object {
6467
hash_reduce(name);
6568
}
6669

70+
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
6771
static constexpr const char* _type_key = "ir.EnvFunc";
6872
static constexpr bool _type_has_method_sequal_reduce = true;
6973
static constexpr bool _type_has_method_shash_reduce = true;

0 commit comments

Comments
 (0)