This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 897be720a6 [Relax] Fix FuseOpsByPattern dropping bound constants on
duplicate-structure functions (#19801)
897be720a6 is described below
commit 897be720a60a9029824bc61ed8a5634bef8fb71f
Author: Shushi Hong <[email protected]>
AuthorDate: Tue Jun 16 20:13:43 2026 -0400
[Relax] Fix FuseOpsByPattern dropping bound constants on
duplicate-structure functions (#19801)
BlockBuilder::AddFunction deduplicates context functions through a map
whose hash (StructuralHashIgnoreNDarray) intentionally ignores tensor
content for speed. Its equality, however, was the default
ffi::StructuralEqual, whose operator() also skips tensor content. As a
result two grouped functions that differ only in their bound constants
-- e.g. two conv layers with different weights produced by
FuseOpsByPattern(bind_constants=True) -- compared equal and were merged,
silently dropping all but the first constant and miscompiling the
result.
A hash may be approximate, but the dedup equality must be exact. This pr
adds a value-aware equality functor (skip_tensor_content=false) and use
it as the map's KeyEqual, keeping the fast content-ignoring hash.
Functions with identical constants still dedup; functions with differing
constants no longer do.
---
src/relax/ir/block_builder.cc | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/relax/ir/block_builder.cc b/src/relax/ir/block_builder.cc
index f9360c6c42..fe6887a97e 100644
--- a/src/relax/ir/block_builder.cc
+++ b/src/relax/ir/block_builder.cc
@@ -429,6 +429,20 @@ class BlockBuilderImpl : public BlockBuilderNode {
}
};
+ /*!
+ * \brief Structural equality that DOES compare tensor (constant) data
content. The hash above
+ * intentionally ignores tensor content for speed, but the equality must
stay exact: otherwise two
+ * grouped functions that differ only in their bound constants (e.g. two
conv layers with
+ * different weights) would be incorrectly treated as duplicates and merged.
+ */
+ class StructuralEqualConsiderNDarray {
+ public:
+ bool operator()(const ffi::ObjectRef& lhs, const ffi::ObjectRef& rhs)
const {
+ return ffi::StructuralEqual::Equal(lhs, rhs, /*map_free_vars=*/false,
+ /*skip_tensor_content=*/false);
+ }
+ };
+
/*!
* \brief A hashmap to store the mapping of Relax functions and TIR PrimFuncs
* in context_mod to their GlobalVar to avoid generating duplicated
functions.
@@ -436,7 +450,7 @@ class BlockBuilderImpl : public BlockBuilderNode {
*/
std::unique_ptr<std::unordered_map<
BaseFunc, std::unordered_set<GlobalVar, ffi::ObjectPtrHash,
ffi::ObjectPtrEqual>,
- StructuralHashIgnoreNDarray, ffi::StructuralEqual>>
+ StructuralHashIgnoreNDarray, StructuralEqualConsiderNDarray>>
ctx_func_dedup_map_ = nullptr;
/*!
@@ -446,7 +460,7 @@ class BlockBuilderImpl : public BlockBuilderNode {
if (ctx_func_dedup_map_ != nullptr) return;
ctx_func_dedup_map_ = std::make_unique<std::unordered_map<
BaseFunc, std::unordered_set<GlobalVar, ffi::ObjectPtrHash,
ffi::ObjectPtrEqual>,
- StructuralHashIgnoreNDarray, ffi::StructuralEqual>>();
+ StructuralHashIgnoreNDarray, StructuralEqualConsiderNDarray>>();
for (const auto& kv : context_mod_->functions) {
const GlobalVar gv = kv.first;
const BaseFunc func = kv.second;