Skip to content

[Fix][Relax] Skip FuseOps groups that have no call to fuse - #20589

Open
wwoosshh wants to merge 1 commit into
apache:mainfrom
wwoosshh:fix/20194-fuseops-call-free-group
Open

wwoosshh wants to merge 1 commit into
apache:mainfrom
wwoosshh:fix/20194-fuseops-call-free-group

Conversation

@wwoosshh

@wwoosshh wwoosshh commented Oct 7, 2026

Copy link
Copy Markdown

Fixes #20194.

FuseOps treats TupleGetItem as injective, so two chained TupleGetItem bindings form a group with two nodes, as a and b do here:

inner = (x, x)
outer = (inner, x)
a = outer[0]
b = a[0]

Only single-node groups were left unfused, so FuseOps created a Primitive function for this group. FunctionCreator then moves the first TupleGetItem to the caller to pass the used tuple field directly, which leaves a function with no call_tir and the default name fused. FuseTIR names the fused PrimFunc after the PrimFuncs it calls, so it rejects that function with Check failed: (func_info_.global_name != "fused"). The nested tuple case in the issue hits this with the LLVM default pipeline.

This PR keeps the bindings of a group without any call as they are, the same way as a single-node group, since there is nothing to fuse. Groups created by FuseOpsByPattern carry attributes and are not affected.

Testing: added test_tuple_get_item_chain_without_call to tests/python/relax/test_transform_fuse_ops.py. It fails on main, where FuseOps creates the fused function, and passes with this change. test_transform_fuse_ops.py (33 tests), test_transform_fuse_tir.py (35) and test_transform_fuse_ops_by_pattern.py (29) pass, and the reproducer from the issue now builds and runs with the LLVM default pipeline for both tuple depths.

Generated-by: Claude Code (Claude Opus 5.5)

FuseOps treats TupleGetItem as injective, so two chained TupleGetItem
bindings, e.g. `a = outer[0]; b = a[0]`, form a group with two nodes.
Only single-node groups were left unfused, so a Primitive function was
created for this group. FunctionCreator then moves the first
TupleGetItem to the caller to pass the used tuple field directly, which
leaves a function with no call_tir and the default name "fused". FuseTIR
names the fused PrimFunc after the PrimFuncs it calls, and rejects such
a function with `Check failed: (func_info_.global_name != "fused")`.

A group without any call has nothing to fuse, so keep its bindings as
they are, the same way as a single-node group. Groups created by
FuseOpsByPattern carry attributes and are not affected.

Fixes apache#20194

Generated-by: Claude Code (Claude Opus 5.5)
@wwoosshh

wwoosshh commented Oct 7, 2026

Copy link
Copy Markdown
Author

cc @tlopex @tqchen

This fixes #20194: FuseOps no longer creates a fused function for a group that has no call, such as a chain of TupleGetItem, which FuseTIR could not lower. Could you take a look when you have time?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug][Relax] FuseTIR crashes when FuseOps creates a private function named fused for nested tuple getitem[Bug]

1 participant