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 d08e468d8d [REFACTOR][IR] Generalize staging lambdas and explicit 
thread reductions (#20552)
d08e468d8d is described below

commit d08e468d8d490143247d7d57b49ac484c2920302
Author: Tianqi Chen <[email protected]>
AuthorDate: Tue Oct 6 07:44:53 2026 -0400

    [REFACTOR][IR] Generalize staging lambdas and explicit thread reductions 
(#20552)
---
 include/tvm/ir/base_expr.h                         |  14 +
 include/tvm/ir/expr.h                              |  42 +++
 include/tvm/ir/expr_functor.h                      |   4 +
 include/tvm/s_tir/stmt.h                           |   3 -
 include/tvm/tirx/builtin.h                         |  32 ++-
 include/tvm/tirx/tile_primitive.h                  |  45 ----
 python/tvm/ir/__init__.py                          |   2 +
 python/tvm/ir/expr.py                              |  85 ++++++
 .../tvm/relax/frontend/nn/llm/_decode_kernels.py   |   7 +-
 python/tvm/script/ir_builder/__init__.py           |   4 +-
 python/tvm/script/ir_builder/ir.py                 |  11 +-
 python/tvm/tirx/__init__.py                        |   2 +-
 python/tvm/tirx/op.py                              |  37 ++-
 python/tvm/tirx/script/ir_builder/ir.py            |   3 +-
 python/tvm/tirx/script/ir_builder/tirx.py          |  10 +-
 python/tvm/tirx/tile_primitive.py                  |  25 +-
 src/ir/expr.cc                                     |  38 +++
 .../transform/lower_cross_thread_reduction.cc      |  45 ++--
 src/script/printer/ir/prim_expr.cc                 |  20 ++
 src/script/printer/script_printer.cc               |   2 +-
 src/tirx/ir/lambdaexpr.cc                          | 144 ----------
 src/tirx/ir/tir_visitor_with_path.cc               |   2 +-
 src/tirx/op/builtin.cc                             |   9 +-
 src/tirx/script/printer/expr.cc                    |  14 -
 src/tirx/transform/lower_thread_allreduce.h        |  71 +++--
 src/tirx/transform/unsupported_dtype_legalize.cc   |  81 +++---
 tests/python/codegen/test_gpu_codegen_allreduce.py |  18 +-
 tests/python/codegen/test_target_codegen_cuda.py   |  28 +-
 .../python/codegen/test_target_codegen_cuda_fp8.py |  14 +-
 .../s_tir/script/test_s_tir_script_printer.py      | 138 +++++-----
 .../test_s_tir_transform_lift_thread_binding.py    |   6 +-
 ...s_tir_transform_lower_cross_thread_reduction.py | 284 +++++++++-----------
 ...test_s_tir_transform_lower_thread_all_reduce.py | 290 +++++++++------------
 .../transform/test_s_tir_transform_thread_sync.py  |  34 +--
 tests/python/tirx-base/test_tir_op_types.py        |  11 +-
 .../test_tir_transform_bf16_legalize.py            | 120 ++++-----
 36 files changed, 802 insertions(+), 893 deletions(-)

diff --git a/include/tvm/ir/base_expr.h b/include/tvm/ir/base_expr.h
index 9ce81b1978..74e9c7a65f 100644
--- a/include/tvm/ir/base_expr.h
+++ b/include/tvm/ir/base_expr.h
@@ -371,6 +371,20 @@ class Expr : public ffi::ObjectRef {
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Expr, ffi::ObjectRef, 
ExprNode);
 };
 
+/*! \brief Base node for traversable expressions eliminated during 
compilation. */
+class StagingExprNode : public ExprNode {
+ public:
+  static void RegisterReflection() { 
ffi::reflection::ObjectDef<StagingExprNode>(); }
+  static constexpr const uint32_t _type_child_slots = 4;
+  TVM_FFI_DECLARE_OBJECT_INFO("ir.StagingExpr", StagingExprNode, ExprNode);
+};
+
+/*! \brief Managed reference to a staging expression. */
+class StagingExpr : public Expr {
+ public:
+  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(StagingExpr, Expr, 
StagingExprNode);
+};
+
 /*!
  * \brief Base node for opaque construction-time expressions.
  *
diff --git a/include/tvm/ir/expr.h b/include/tvm/ir/expr.h
index aa4b2dd44b..0936618f4d 100644
--- a/include/tvm/ir/expr.h
+++ b/include/tvm/ir/expr.h
@@ -438,6 +438,48 @@ class PrimVar : public PrimExpr {
   static constexpr bool _type_container_is_exact = false;
 };
 
+/*!
+ * \brief A typed staging expression representing a lambda computation.
+ *
+ * LambdaExpr records computations such as reduction combiners and predication
+ * rules. Its body may describe computations on runtime values.
+ *
+ * Parameters are bound within the expression body, which may produce a scalar
+ * or tuple result. The lambda has a FuncType describing its parameter and
+ * return types.
+ *
+ * As a StagingExpr, LambdaExpr is eliminated during compilation and does not
+ * remain in executable IR.
+ */
+class LambdaExprNode : public StagingExprNode {
+ public:
+  /*! \brief Lambda-local parameter definitions. */
+  ffi::Array<Var> vars;
+  /*! \brief Computation over the parameters and captured expressions. */
+  Expr body;
+
+  explicit LambdaExprNode(ffi::UnsafeInit tag) : body(tag) {}
+  explicit LambdaExprNode(Expr body) : body(std::move(body)) {}
+  /*! \brief Simultaneously substitute arguments for the bound parameters. */
+  TVM_DLL Expr Apply(const ffi::Array<Expr>& arguments) const;
+
+  static void RegisterReflection() {
+    namespace refl = tvm::ffi::reflection;
+    refl::ObjectDef<LambdaExprNode>()
+        .def_ro("vars", &LambdaExprNode::vars, 
refl::AttachFieldFlag::SEqHashDefSimple())
+        .def_ro("body", &LambdaExprNode::body);
+  }
+  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindTreeNode;
+  TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.LambdaExpr", LambdaExprNode, 
StagingExprNode);
+};
+
+/*! \brief Managed reference to a typed staging lambda. */
+class LambdaExpr : public StagingExpr {
+ public:
+  TVM_DLL explicit LambdaExpr(ffi::Array<Var> vars, Expr body);
+  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(LambdaExpr, StagingExpr, 
LambdaExprNode);
+};
+
 class GlobalVar;
 /*!
  * \brief Global variable that lives in the top-level module.
diff --git a/include/tvm/ir/expr_functor.h b/include/tvm/ir/expr_functor.h
index 95bd226a77..64df3d6e76 100644
--- a/include/tvm/ir/expr_functor.h
+++ b/include/tvm/ir/expr_functor.h
@@ -73,6 +73,9 @@ class ExprFunctor<R(const Expr&, Args...)> {
     return (*vtable_)(node, this, std::forward<Args>(args)...);
   }
 
+  virtual R Dispatch_(const LambdaExprNode* node, Args... args) {
+    return DispatchDefault_(node, std::forward<Args>(args)...);
+  }
   virtual R Dispatch_(const OpaqueExprNode* node, Args... args) {
     return DispatchDefault_(node, std::forward<Args>(args)...);
   }
@@ -230,6 +233,7 @@ class ExprFunctor<R(const Expr&, Args...)> {
    * \param vtable The table to initialize before adding derived registrations.
    */
   static void InitVTable(VTable* vtable) {
+    SetDispatch<TSelf, LambdaExprNode>(vtable);
     SetDispatch<TSelf, OpaqueExprNode>(vtable);
     SetDispatch<TSelf, TupleNode>(vtable);
     SetDispatch<TSelf, TupleGetItemNode>(vtable);
diff --git a/include/tvm/s_tir/stmt.h b/include/tvm/s_tir/stmt.h
index 3edce701ac..5685694ec2 100644
--- a/include/tvm/s_tir/stmt.h
+++ b/include/tvm/s_tir/stmt.h
@@ -254,9 +254,6 @@ constexpr const char* fragment_layout = "fragment_layout";
  */
 constexpr const char* loop_partition_hint = "loop_partition_hint";
 
-/*! \brief Mark of reduce scope */
-constexpr const char* reduce_scope = "reduce_scope";
-
 // -----------------------------------------------------------------------
 // meta_schedule annotations
 // -----------------------------------------------------------------------
diff --git a/include/tvm/tirx/builtin.h b/include/tvm/tirx/builtin.h
index 27033de716..784753f1c7 100644
--- a/include/tvm/tirx/builtin.h
+++ b/include/tvm/tirx/builtin.h
@@ -500,17 +500,33 @@ TVM_DLL const Op& tvm_warp_shuffle_xor();
 TVM_DLL const Op& tvm_warp_activemask();
 
 /*!
- * \brief See pesudo code
+ * \brief Cross-thread reduction with an explicit typed combiner and 
identities.
  *
- *  void tvm_thread_allreduce(UIntImm size, Expr source0, ..., Expr cond,
- *                            Var reduce_temp0, .., Var thread_idx1, ...) {
- *     // constraint by the other thread_idx remain the same.
- *     // reduce_temp is used to save intermediate result.
- *     reduce_temp0, ... = reduce(combiner, source0, ..., cond
- *       over [thread_idx1, thread_idx2] passed by any caller)
- *  }
+ * void tvm_thread_allreduce(LambdaExpr combine, Expr identity, Expr values,
+ *                           PrimExpr predicate, Expr destinations, Expr 
thread_axes);
+ *
+ * For N values, combine binds lhs[0:N] followed by rhs[0:N] and returns an
+ * N-element Tuple, or a scalar when N is one. Identity, values, destinations
+ * and thread_axes may each be a scalar or an explicit Tuple of fields.
+ * Each value, identity, pair of parameters and result have
+ * the same primitive type. Inactive inputs are replaced by their identities.
+ * Destinations are N tensor loads (optionally cast for boolean storage), and
+ * thread_axes are reduction thread variables or zero for simplified unit axes.
+ * Other thread indices remain fixed. The operation writes the reduced values
+ * to the destination tensors and returns void.
  */
 TVM_DLL const Op& tvm_thread_allreduce();
+
+/*!
+ * \brief View a scalar all-reduce operand/result as one field, or expose its 
Tuple fields.
+ * \param value The scalar expression or explicit Tuple.
+ * \return The fields without changing the expression's representation in IR.
+ */
+inline ffi::Array<Expr> GetAllreduceFields(const Expr& value) {
+  if (const auto* tuple = value.as<tvm::TupleNode>()) return tuple->fields;
+  return {value};
+}
+
 // Metal cooperative_tensor intrinsics (MetalPerformancePrimitives / Metal 4)
 
 /*!
diff --git a/include/tvm/tirx/tile_primitive.h 
b/include/tvm/tirx/tile_primitive.h
index 70568d5c4e..1716e29a03 100644
--- a/include/tvm/tirx/tile_primitive.h
+++ b/include/tvm/tirx/tile_primitive.h
@@ -33,51 +33,6 @@
 namespace tvm {
 namespace tirx {
 
-/*!
- * \brief A reified Python lambda: a list of bound variables and a body over 
them.
- *
- * Used by tile primitive ops that take a per-element expression over the
- * destination axes (e.g. ``tirx.tile.select``). ``vars`` are the abstract
- * axis variables (lambda-bound); ``pred`` is the body referencing them.
- * At lowering time the dispatch substitutes ``vars`` with the concrete
- * instruction axes via ``Apply``.
- */
-class LambdaExprNode : public ExprNode {
- public:
-  explicit LambdaExprNode(ffi::UnsafeInit tag) : pred(tag) {}
-
-  explicit LambdaExprNode(PrimExpr pred) : pred(std::move(pred)) {}
-
-  /*! \brief The bound variables of the lambda. */
-  Array<Var> vars;
-  /*! \brief The lambda body over ``vars``. */
-  PrimExpr pred;
-
-  /*! \brief Replace the bound variables with the given indices, returning the 
substituted body. */
-  PrimExpr Apply(const Array<PrimExpr>& indices) const;
-
-  static void RegisterReflection() {
-    namespace refl = tvm::ffi::reflection;
-    refl::ObjectDef<LambdaExprNode>()
-        .def_ro("vars", &LambdaExprNode::vars, 
refl::AttachFieldFlag::SEqHashDefPattern())
-        .def_ro("pred", &LambdaExprNode::pred);
-  }
-
-  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindTreeNode;
-  TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.LambdaExpr", LambdaExprNode, 
ExprNode);
-};
-
-/*!
- * \brief Managed reference to LambdaExprNode.
- * \sa LambdaExprNode
- */
-class LambdaExpr : public Expr {
- public:
-  explicit LambdaExpr(Array<Var> vars, PrimExpr pred);
-
-  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(LambdaExpr, Expr, 
LambdaExprNode);
-};
-
 /*!
  * \brief The type of the function that sanitizes the arguments of a TIRX 
operator.
  * \param op The operator.
diff --git a/python/tvm/ir/__init__.py b/python/tvm/ir/__init__.py
index 7703a6dc40..7151ef6f1e 100644
--- a/python/tvm/ir/__init__.py
+++ b/python/tvm/ir/__init__.py
@@ -54,6 +54,8 @@ from .expr import (
     ExprOperand,
     ExprWithOp,
     GlobalVar,
+    LambdaExpr,
+    StagingExpr,
     OpaqueExpr,
     Range,
     TensorLoad,
diff --git a/python/tvm/ir/expr.py b/python/tvm/ir/expr.py
index 1177f52c56..9d67db2683 100644
--- a/python/tvm/ir/expr.py
+++ b/python/tvm/ir/expr.py
@@ -16,6 +16,9 @@
 # under the License.
 """Common expressions data structures in the IR."""
 
+from collections.abc import Callable
+from numbers import Number
+
 import tvm_ffi
 
 import tvm
@@ -59,6 +62,11 @@ class Expr(Node):
         )
 
 
+@tvm_ffi.register_object("ir.StagingExpr")
+class StagingExpr(Expr):
+    """A traversable expression eliminated before executable IR."""
+
+
 @tvm_ffi.register_object("ir.OpaqueExpr")
 class OpaqueExpr(Expr):
     """Base class for opaque values that must be removed from finished IR."""
@@ -681,6 +689,83 @@ class Var(_CallableExprWithOp):
         self.__init_handle_by_constructor__(_ffi_api.Var, name, ty, span)
 
 
+def _lambda_type(annotation):
+    """Normalize explicit types and the script scalar constructor forms."""
+    if isinstance(annotation, tvm.ir.Type):
+        return annotation
+    if isinstance(annotation, str):
+        return Var("", annotation).ty
+    if isinstance(annotation, tvm.DataType):
+        return tvm.ir.PrimType(annotation)
+    dtype = getattr(annotation, "_dtype_str", None)
+    if dtype is not None:
+        return tvm.ir.PrimType(dtype)
+    if callable(annotation):
+        value = annotation()
+        if isinstance(value, Expr):
+            return value.ty
+        if isinstance(value, tvm.ir.Type):
+            return value
+    raise TypeError("Lambda parameter and return annotations must be explicit 
IR types")
+
+
+def _lambda_result(value):
+    if isinstance(value, tuple | list):
+        return Tuple([_lambda_result(field) for field in value])
+    if isinstance(value, Number):
+        value = const(value)
+    elif isinstance(value, str):
+        value = StringImm(value)
+    else:
+        value = tvm.runtime.convert(value)
+    if not isinstance(value, Expr):
+        raise TypeError("Lambda body must be an Expr or a tuple/list of 
expressions")
+    return value
+
+
+@tvm_ffi.register_object("ir.LambdaExpr")
+class LambdaExpr(StagingExpr, Scriptable):
+    """A typed staging expression representing a lambda computation.
+
+    LambdaExpr records computations such as reduction combiners and predication
+    rules. Its body may describe computations on runtime values.
+
+    Parameters are bound within the expression body, which may produce a scalar
+    or tuple result. The lambda has a FuncType describing its parameter and
+    return types.
+
+    As a StagingExpr, LambdaExpr is eliminated during compilation and does not
+    remain in executable IR.
+
+    Parameters
+    ----------
+    parameter_types : list[Type]
+        Explicit parameter types, in callable argument order. Primitive dtype
+        strings and script scalar constructors are also accepted.
+    function : Callable
+        A callable evaluated once with one fresh typed Var per supplied type.
+        Tuple/list results become shared IR Tuple expressions.
+    ret_type : Type, optional
+        An exact return-type check. No implicit conversion or cast is inserted.
+    """
+
+    vars: list[Var]
+    body: Expr
+
+    def __init__(self, parameter_types, function: Callable, *, ret_type=None):
+        variables = [Var(f"arg{i}", _lambda_type(ty)) for i, ty in 
enumerate(parameter_types)]
+        body = _lambda_result(function(*variables))
+        if ret_type is not None:
+            expected = _lambda_type(ret_type)
+            if not tvm_ffi.structural_equal(expected, body.ty):
+                raise TypeError("LambdaExpr return annotation does not match 
the body type")
+        self.__init_handle_by_constructor__(_ffi_api.LambdaExpr, variables, 
body)
+
+    def apply(self, arguments: list[Expr]) -> Expr:
+        """Substitute arguments simultaneously for the lambda's bound 
variables."""
+        return _ffi_api.LambdaExprApply(self, [_lambda_result(arg) for arg in 
arguments])
+
+
 @tvm_ffi.register_object("ir.Range")
 class Range(Node, Scriptable):
     """Represent a range in TVM.
diff --git a/python/tvm/relax/frontend/nn/llm/_decode_kernels.py 
b/python/tvm/relax/frontend/nn/llm/_decode_kernels.py
index 83bba4dfe2..bb38cf90f7 100644
--- a/python/tvm/relax/frontend/nn/llm/_decode_kernels.py
+++ b/python/tvm/relax/frontend/nn/llm/_decode_kernels.py
@@ -324,12 +324,7 @@ def _attention_decode(num_kv_heads, num_qo_heads, 
head_dim, qkv_dtype, sliding_w
                                         with Ts.sblock("block_cross_thread"):
                                             Ts.reads(S_reduce_local[0])
                                             Ts.writes(t0[0])
-                                            T.attr(
-                                                T.comm_reducer(lambda x0, y0: 
x0 + y0, [T.float32(0)]),
-                                                "reduce_scope",
-                                                T.int32(0),
-                                            )
-                                            
T.tvm_thread_allreduce(T.uint32(1), S_reduce_local[0], True, t0[0], tx)
+                                            
T.tvm_thread_allreduce(T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + 
y0,)), (T.float32(0),), (S_reduce_local[0],), True, (t0[0],), (tx,))
 
                                         S_local[j] = -5e4
                                         if (iterator * bdz + tz) * bdy * 
tile_size_per_bdx + j < kv_chunk_len[0]:
diff --git a/python/tvm/script/ir_builder/__init__.py 
b/python/tvm/script/ir_builder/__init__.py
index 30f92ab098..327a55048b 100644
--- a/python/tvm/script/ir_builder/__init__.py
+++ b/python/tvm/script/ir_builder/__init__.py
@@ -39,8 +39,8 @@ from .base import (
     with_at_group_,
 )
 from .frame import IRModuleFrame
-from .ir import _get_dialect_builder as __getattr__
 from .ir import (
+    Lambda,
     constexpr,
     dtype,
     dynamic,
@@ -50,6 +50,7 @@ from .ir import (
     module_global_infos,
     module_set_attr,
 )
+from .ir import _get_dialect_builder as __getattr__
 from .parser_protocol import (
     check_well_formed_,
     decl_function,
@@ -68,6 +69,7 @@ __all__ = [
     "GenericConst",
     "IRBuilder",
     "IRModuleFrame",
+    "Lambda",
     "MissingType",
     "PrimType",
     "Range",
diff --git a/python/tvm/script/ir_builder/ir.py 
b/python/tvm/script/ir_builder/ir.py
index 6b31d7b174..d7f1b40dae 100644
--- a/python/tvm/script/ir_builder/ir.py
+++ b/python/tvm/script/ir_builder/ir.py
@@ -19,7 +19,7 @@
 from typing import NoReturn, TypeVar
 
 from tvm import DataType
-from tvm.ir import DataTypeImm, GlobalInfo, Span, Var
+from tvm.ir import DataTypeImm, GlobalInfo, LambdaExpr, Span, Var
 from tvm.runtime import Object as tvm_Object
 
 from . import _ffi_api
@@ -189,3 +189,12 @@ def _get_dialect_builder(name: str):
         setattr(sys.modules["tvm.script.ir_builder"], name, module)
         return module
     raise AttributeError(f"module 'tvm.script.ir_builder' has no attribute 
{name!r}")
+
+
+def Lambda(parameter_types, function, *, ret_type=None):  # pylint: 
disable=invalid-name
+    """Build a shared staging lambda from explicit types and a Python callable.
+
+    Scalar constructors such as ``T.float32`` may be used as parameter types.
+    An optional return annotation checks the body type without inserting casts.
+    """
+    return LambdaExpr(parameter_types, function, ret_type=ret_type)
diff --git a/python/tvm/tirx/__init__.py b/python/tvm/tirx/__init__.py
index 705540f31e..01989b0a35 100644
--- a/python/tvm/tirx/__init__.py
+++ b/python/tvm/tirx/__init__.py
@@ -54,7 +54,7 @@ from .stmt import SeqStmt
 from .stmt import IfThenElse, Evaluate, stmt_seq, stmt_list
 from .stmt import BufferRegion, BufferRegionType
 from .stmt import ScopeIdDefStmt
-from .tile_primitive import DispatchContext, LambdaExpr, TilePrimitiveCall
+from .tile_primitive import DispatchContext, TilePrimitiveCall
 
 from .function import PrimFunc, IndexMap
 
diff --git a/python/tvm/tirx/op.py b/python/tvm/tirx/op.py
index 9e3dd06fd3..6220d308da 100644
--- a/python/tvm/tirx/op.py
+++ b/python/tvm/tirx/op.py
@@ -811,20 +811,45 @@ def address_of(obj: Var | TensorLoad, span: Span | None = 
None) -> Expr:
         raise ValueError(f"Invalid object type: {type(obj)}")
 
 
-def tvm_thread_allreduce(*freduce_args):
-    """Perform allreduce inside threadblock.
+def tvm_thread_allreduce(combine, identity, values, predicate, destinations, 
thread_axes):
+    """Perform an all-reduce inside a thread block.
 
     Parameters
     ----------
-    freduce_args : Expr
-        The args.
+    combine : tvm.ir.LambdaExpr
+        Typed combining lambda with parameters ordered as all left-hand values
+        followed by all right-hand values. Its body returns a scalar for one
+        result or a Tuple of results.
+    identity : Expr or Sequence[Expr]
+        Identity value for each reduction result.
+    values : Expr or Sequence[Expr]
+        Values contributed by the current thread.
+    predicate : PrimExpr
+        Boolean participation predicate. Inactive threads contribute 
identities.
+    destinations : Expr or Sequence[Expr]
+        Tensor loads identifying the destinations of the reduction results.
+    thread_axes : Expr or Sequence[Expr]
+        Thread variables participating in the reduction.
 
     Returns
     -------
     call : Expr
-        The call expression.
+        The void call expression with six explicit operands.
     """
-    return call_intrin("void", "tirx.tvm_thread_allreduce", *freduce_args)
+
+    def as_operand(value):
+        return tvm.ir.Tuple(value) if isinstance(value, list | tuple | Array) 
else value
+
+    return call_intrin(
+        "void",
+        "tirx.tvm_thread_allreduce",
+        combine,
+        as_operand(identity),
+        as_operand(values),
+        predicate,
+        as_operand(destinations),
+        as_operand(thread_axes),
+    )
 
 
 def tvm_thread_invariant(cond):
diff --git a/python/tvm/tirx/script/ir_builder/ir.py 
b/python/tvm/tirx/script/ir_builder/ir.py
index fceb8ddbe3..372b8c9193 100644
--- a/python/tvm/tirx/script/ir_builder/ir.py
+++ b/python/tvm/tirx/script/ir_builder/ir.py
@@ -38,7 +38,7 @@ from tvm import DataType, ir
 from tvm import tirx as tir
 from tvm.ir import Range, Type, is_prim_expr
 from tvm.script.ir_builder.base import MISSING, IRBuilder
-from tvm.script.ir_builder.ir import meta_var
+from tvm.script.ir_builder.ir import Lambda, meta_var
 from tvm.script.parser.protocol_registry import (
     register_mutable_decl as _register_mutable_decl,
 )
@@ -1613,6 +1613,7 @@ __all__ = [
     "IntImm",
     "Iter",
     "IterVar",
+    "Lambda",
     "Layout",
     "LetAnnotation",
     "LocalVectorAnnotation",
diff --git a/python/tvm/tirx/script/ir_builder/tirx.py 
b/python/tvm/tirx/script/ir_builder/tirx.py
index 2348a88da7..e9cec36ff6 100644
--- a/python/tvm/tirx/script/ir_builder/tirx.py
+++ b/python/tvm/tirx/script/ir_builder/tirx.py
@@ -21,8 +21,8 @@ from collections.abc import Callable
 
 import tvm
 import tvm.tirx.operator as tirx_op
-from tvm.ir import Op, TensorRegion
-from tvm.tirx import Expr, LambdaExpr, Var, buffer_data, is_tensor_var
+from tvm.ir import LambdaExpr, Op, PrimType, TensorRegion
+from tvm.tirx import Expr, Var, buffer_data, is_tensor_var
 from tvm.tirx.exec_scope import _SCOPE_KIND_TO_NAME, ExecScope
 from tvm.tirx.expr import FloatImm, IntImm
 from tvm.tirx.lang.alloc_pool import SMEMPool, TMEMPool
@@ -1679,7 +1679,11 @@ def select(
     if is_tensor_var(false_value):
         false_value = _to_region(false_value)
     if not isinstance(pred, LambdaExpr):
-        pred = LambdaExpr(pred)
+        pred = LambdaExpr([PrimType("int32")] * len(dst.region), pred)
+    if len(pred.vars) != len(dst.region) or any(var.ty != PrimType("int32") 
for var in pred.vars):
+        raise TypeError("Tile select requires one int32 lambda parameter per 
destination axis")
+    if pred.ty.ret_type != PrimType("bool"):
+        raise TypeError("Tile select requires a scalar boolean lambda result")
     return f_insert(tirx_op.Select(dst, true_value, false_value, pred, 
scope=scope))
 
 
diff --git a/python/tvm/tirx/tile_primitive.py 
b/python/tvm/tirx/tile_primitive.py
index 94c390f03c..48049f4a0f 100644
--- a/python/tvm/tirx/tile_primitive.py
+++ b/python/tvm/tirx/tile_primitive.py
@@ -14,14 +14,12 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-"""TIRx tile primitive IR nodes: LambdaExpr, DispatchContext, 
TilePrimitiveCall.
+"""TIRx tile primitive IR nodes: DispatchContext, TilePrimitiveCall.
 
 Mirrors the C++ header ``include/tvm/tirx/tile_primitive.h``.
 """
 # pylint: disable=no-member
 
-import inspect
-from collections.abc import Callable
 from typing import Any, ClassVar
 
 import tvm_ffi
@@ -37,27 +35,6 @@ from .expr import FloatImm, IterVar
 from .stmt import Stmt
 
 
-@register_object("tirx.LambdaExpr")
-class LambdaExpr(Expr):
-    """A reified Python lambda: bound variables and a body over them.
-
-    Used by tile primitive ops that take a per-element expression over the
-    destination axes (e.g. ``tirx.tile.select``).
-    """
-
-    vars: list[Var]
-    pred: Expr
-
-    def __init__(self, f_pred: Callable[..., Expr]):
-        vars = [Var(name, "int32") for name in 
inspect.signature(f_pred).parameters]
-        pred = f_pred(*vars)
-        self.__init_handle_by_constructor__(_ffi_api.LambdaExpr, vars, pred)
-
-    def apply(self, indices: list[Expr]) -> Expr:
-        """Substitute the bound variables with the given indices, returning 
the body."""
-        return _ffi_api.LambdaExprApply(self, indices)
-
-
 @register_object("tirx.DispatchContext")
 class DispatchContext(Object, Scriptable):
     """DispatchContext node.
diff --git a/src/ir/expr.cc b/src/ir/expr.cc
index 7d07ab95d9..6ab088995e 100644
--- a/src/ir/expr.cc
+++ b/src/ir/expr.cc
@@ -720,6 +720,44 @@ TVM_FFI_STATIC_INIT_BLOCK() {
                         });
 }
 
+// LambdaExpr
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+  StagingExprNode::RegisterReflection();
+  LambdaExprNode::RegisterReflection();
+}
+
+Expr LambdaExprNode::Apply(const ffi::Array<Expr>& arguments) const {
+  TVM_FFI_CHECK_EQ(arguments.size(), vars.size(), ValueError) << "LambdaExpr 
Apply arity mismatch";
+  ffi::Map<Var, Expr> vmap;
+  for (size_t i = 0; i < vars.size(); ++i) vmap.Set(vars[i], arguments[i]);
+  return ffi::StructuralMap<ffi::WalkOrder::kPostOrder>(
+             body, [&](const Var& var) -> Expr { return 
vmap.Get(var).value_or(var); })
+      .cast<Expr>();
+}
+
+LambdaExpr::LambdaExpr(ffi::Array<Var> vars, Expr body) : 
StagingExpr(ffi::UnsafeInit{}) {
+  auto n = ffi::make_object<LambdaExprNode>(std::move(body));
+  ffi::Array<Type> types;
+  for (const Var& var : vars) types.push_back(var->ty);
+  n->ty = FuncType(types, n->body->ty);
+  n->vars = std::move(vars);
+  data_ = std::move(n);
+}
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::GlobalDef().def("ir.LambdaExpr",
+                        [](ffi::Array<Var> vars, Expr body) { return 
LambdaExpr(vars, body); });
+}
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::GlobalDef().def("ir.LambdaExprApply", [](LambdaExpr body, 
ffi::Array<Expr> indices) {
+    return body->Apply(indices);
+  });
+}
+
 // Tuple
 Tuple::Tuple(ffi::Array<Expr> fields, Span span) : Expr(ffi::UnsafeInit{}) {
   ffi::Optional<Type> tuple_ty = [&]() -> ffi::Optional<Type> {
diff --git a/src/s_tir/transform/lower_cross_thread_reduction.cc 
b/src/s_tir/transform/lower_cross_thread_reduction.cc
index 098ebdf119..fde0ac24f7 100644
--- a/src/s_tir/transform/lower_cross_thread_reduction.cc
+++ b/src/s_tir/transform/lower_cross_thread_reduction.cc
@@ -24,6 +24,7 @@
 #include <tvm/ffi/extra/structural_mutate.h>
 #include <tvm/ffi/extra/structural_visit.h>
 #include <tvm/ffi/reflection/registry.h>
+#include <tvm/ir/expr.h>
 #include <tvm/s_tir/analysis.h>
 #include <tvm/s_tir/stmt.h>
 #include <tvm/s_tir/stmt_functor.h>
@@ -410,31 +411,35 @@ Stmt TransformReductionBlock(const SBlockRealizeNode* 
realize,
   }
   // Stmt 3: do cross-thread reduction
   {
-    // Step 3.1. Create the parameters to the intrinsic
-    ffi::Array<PrimExpr> parameters;
-    parameters.reserve(reduction_loops.size() + 4);
-    // 1-st argument: number of buffers
-    parameters.push_back(IntImm(PrimType::UInt(32), n_buffers));
-    // Next `n_buffers` arguments: sources
+    // Step 3.1. Carry the reducer directly as typed staging operands.
+    ffi::Array<Var> combine_vars;
+    for (const Var& var : reducer->lhs) combine_vars.push_back(var);
+    for (const Var& var : reducer->rhs) combine_vars.push_back(var);
+    LambdaExpr combine(combine_vars, tvm::Tuple(reducer->result));
+    ffi::Array<PrimExpr> values;
     if (it_buffers.has_value()) {
       for (int i = 0; i < n_buffers; ++i) {
-        parameters.push_back(MakeTensorLoad(it_buffers.value()[i], 
{IntImm::Int32(0)}));
+        values.push_back(MakeTensorLoad(it_buffers.value()[i], 
{IntImm::Int32(0)}));
       }
     } else {
-      parameters.insert(parameters.end(), combiner_rhs.begin(), 
combiner_rhs.end());
+      values = combiner_rhs;
     }
-    // Next argument: predicate
-    parameters.push_back(IntImm::Bool(true));
-    // Next `n_buffers` arguments: destinations
+    ffi::Array<PrimExpr> destinations;
     for (int i = 0; i < n_buffers; ++i) {
-      parameters.push_back(MakeTensorLoad(ct_buffers[i], {0}));
+      destinations.push_back(MakeTensorLoad(ct_buffers[i], {0}));
     }
-    // Next arguments: all the reduction threads
+    ffi::Array<PrimExpr> thread_axes;
     for (const ForNode* reduction_loop : reduction_loops) {
       if (reduction_loop->thread_binding.has_value()) {
-        parameters.push_back(reduction_loop->loop_var);
+        thread_axes.push_back(reduction_loop->loop_var);
       }
     }
+    ffi::Array<Expr> parameters{combine,
+                                tvm::Tuple(reducer->identity_element),
+                                tvm::Tuple(values),
+                                IntImm::Bool(true),
+                                tvm::Tuple(destinations),
+                                tvm::Tuple(thread_axes)};
     // Step 3.2. Create the block and the block-realize.
     ffi::Array<IterVar> iter_vars{nullptr};
     ffi::Array<PrimExpr> bindings{nullptr};
@@ -457,14 +462,10 @@ Stmt TransformReductionBlock(const SBlockRealizeNode* 
realize,
                /*writes=*/ct_buffer_regions,
                /*name_hint=*/block->name_hint + "_cross_thread",
                /*body=*/
-               AttrStmt(/*node=*/reducer,
-                        /*attr_key=*/s_tir::attr::reduce_scope,
-                        /*value=*/IntImm::Int32(0),
-                        /*body=*/
-                        Evaluate(Call(/*dtype=*/PrimType::Void(),
-                                      
/*op=*/tirx::builtin::tvm_thread_allreduce(),
-                                      /*args=*/std::move(parameters))
-                                     .as_or_throw<PrimExpr>())))));
+               Evaluate(Call(/*dtype=*/PrimType::Void(),
+                             /*op=*/tirx::builtin::tvm_thread_allreduce(),
+                             /*args=*/std::move(parameters))
+                            .as_or_throw<PrimExpr>()))));
   }
   // Stmt 4: write cross-thread reduction result to the original buffer
   {
diff --git a/src/script/printer/ir/prim_expr.cc 
b/src/script/printer/ir/prim_expr.cc
index 9bf33fd3ed..9e0ff08992 100644
--- a/src/script/printer/ir/prim_expr.cc
+++ b/src/script/printer/ir/prim_expr.cc
@@ -29,6 +29,26 @@ namespace script {
 namespace printer {
 namespace details {
 
+ffi::Optional<ExprDoc> LambdaExprDocTranslate(DocTranslatorObj* d, 
ffi::AnyView input,
+                                              const ffi::Object*) {
+  const auto* lambda =
+      ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
LambdaExprNode>(input);
+  VarScope scope(d);
+  ffi::Array<IdDoc> args;
+  ffi::Array<ExprDoc> types;
+  for (const Var& var : lambda->vars) {
+    args.push_back(VarDoc(d, var));
+    types.push_back(TypeValue(d, var->ty, false));
+  }
+  ExprDoc body = d->Translate(lambda->body).value();
+  return NamespaceDoc("ir")->Attr("Lambda")->Call({ListDoc(types), 
LambdaDoc(args, body)});
+}
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+  ffi::reflection::TypeAttrDef<LambdaExprNode>().attr(
+      kDocTranslate, FDocTranslate::FromNative<&LambdaExprDocTranslate>());
+}
+
 namespace {
 
 template <typename T, OperationDocNode::Kind kind, PrimExpr 
(*operation)(PrimExpr, PrimExpr, Span)>
diff --git a/src/script/printer/script_printer.cc 
b/src/script/printer/script_printer.cc
index 13be7ca202..04bbdcb850 100644
--- a/src/script/printer/script_printer.cc
+++ b/src/script/printer/script_printer.cc
@@ -138,7 +138,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   RegisterScriptRepr<tirx::IndexMapNode>();
   RegisterScriptRepr<tirx::IterNode>();
   RegisterScriptRepr<tirx::IterVarNode>();
-  RegisterScriptRepr<tirx::LambdaExprNode>();
+  RegisterScriptRepr<LambdaExprNode>();
   RegisterScriptRepr<tirx::PrimFuncNode>();
   RegisterScriptRepr<tirx::ReturnNode>();
   RegisterScriptRepr<tirx::ScopeIdDefNode>();
diff --git a/src/tirx/ir/lambdaexpr.cc b/src/tirx/ir/lambdaexpr.cc
deleted file mode 100644
index b73e6caab1..0000000000
--- a/src/tirx/ir/lambdaexpr.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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 lambdaexpr.cc
- * \brief Implementation of LambdaExpr, a reified lambda used by tile 
primitive ops.
- */
-
-#include <tvm/ffi/extra/structural_mutate.h>
-#include <tvm/ffi/extra/structural_visit.h>
-#include <tvm/tirx/tile_primitive.h>
-
-#include <utility>
-#include <vector>
-
-namespace tvm {
-namespace tirx {
-
-namespace {
-
-ffi::Expected<ffi::Optional<ffi::VisitInterrupt>> LambdaExprVisit(
-    ffi::StructuralVisitorObj* visitor, ffi::AnyView value) noexcept {
-  const auto* self =
-      ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
LambdaExprNode>(value);
-  TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->VisitExpected(self->ty));
-  TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->WithDefRegionKind(
-      kTVMFFIDefRegionKindSimple, [&]() { return 
visitor->VisitExpected(self->vars); }));
-  return visitor->VisitExpected(self->pred);
-}
-
-ffi::Expected<ffi::UnchangedOr<ffi::Any>> 
LambdaExprMutate(ffi::StructuralMutatorObj* mutator,
-                                                           ffi::AnyView value) 
noexcept {
-  const auto* self =
-      ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
LambdaExprNode>(value);
-  // Parameters shadow substitutions from the surrounding expression. Save and 
restore
-  // their remaps so a binder rewrite stays local to this lambda's body.
-  std::vector<ffi::Any> saved;
-  for (const Var& var : self->vars) {
-    auto previous = mutator->VarRemapGetExpected(var);
-    TVM_FFI_S_MUTATE_MAYBE_EARLY_RETURN(previous);
-    saved.push_back(std::move(previous).value());
-  }
-  auto result = [&]() -> ffi::Expected<ffi::UnchangedOr<ffi::Any>> {
-    for (const Var& var : self->vars) {
-      auto cleared = mutator->VarRemapSetExpected(var, nullptr);
-      TVM_FFI_S_MUTATE_MAYBE_EARLY_RETURN(cleared);
-    }
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
-                                      mutator->MutateExpected(self->ty));
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Var>>, 
mapped_vars,
-                                      
mutator->WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&]() {
-                                        return 
mutator->MutateExpected(self->vars);
-                                      }));
-    auto vars = std::move(mapped_vars).ValueOrUnchanged(self->vars);
-    for (size_t i = 0; i < vars.size(); ++i) {
-      auto remap = mutator->VarRemapSetExpected(self->vars[i], vars[i]);
-      TVM_FFI_S_MUTATE_MAYBE_EARLY_RETURN(remap);
-    }
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_pred,
-                                      mutator->MutateExpected(self->pred));
-    if (vars.same_as(self->vars) && mapped_pred.UnchangedOrSameAs(self->pred) 
&&
-        mapped_ty.UnchangedOrSameAs(self->ty)) {
-      return ffi::Unchanged();
-    }
-    auto copy = ffi::make_object<LambdaExprNode>(*self);
-    copy->vars = std::move(vars);
-    copy->pred = std::move(mapped_pred).ValueOrUnchanged(self->pred);
-    copy->ty = std::move(mapped_ty).ValueOrUnchanged(self->ty);
-    return ffi::Any(std::move(copy));
-  }();
-  for (size_t i = 0; i < self->vars.size(); ++i) {
-    auto restored = mutator->VarRemapSetExpected(self->vars[i], saved[i]);
-    TVM_FFI_S_MUTATE_MAYBE_EARLY_RETURN(restored);
-  }
-  return result;
-}
-
-}  // namespace
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  LambdaExprNode::RegisterReflection();
-  refl::TypeAttrDef<LambdaExprNode>()
-      .attr(refl::type_attr::kStructuralVisit,
-            ffi::FStructuralVisit::FromNative<&LambdaExprVisit>())
-      .attr(refl::type_attr::kStructuralMutate,
-            ffi::FStructuralMutate::FromNative<&LambdaExprMutate>())
-      .attr(refl::type_attr::kStructuralMaybeInplaceMutate,
-            ffi::FStructuralMutate::FromNative<&LambdaExprMutate>());
-}
-
-PrimExpr LambdaExprNode::Apply(const ffi::Array<PrimExpr>& indices) const {
-  TVM_FFI_ICHECK_EQ(indices.size(), vars.size());
-
-  ffi::Map<Var, PrimExpr> vmap;
-
-  for (size_t i = 0; i < vars.size(); i++) {
-    vmap.Set(vars[i], indices[i]);
-  }
-  auto f_substitute = [&vmap](const Var& var) -> 
ffi::Expected<ffi::UnchangedOr<ffi::Any>> {
-    if (auto repl = vmap.Get(var)) return ffi::Any(*std::move(repl));
-    return ffi::Unchanged();
-  };
-  return ffi::StructuralMap<ffi::WalkOrder::kPreOrder>(std::move(pred), 
f_substitute)
-      .as_or_throw<PrimExpr>();
-}
-
-LambdaExpr::LambdaExpr(ffi::Array<Var> vars, PrimExpr pred) : 
Expr(ffi::UnsafeInit{}) {
-  auto n = ffi::make_object<LambdaExprNode>(std::move(pred));
-  n->vars = std::move(vars);
-  data_ = std::move(n);
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("tirx.LambdaExpr",
-                        [](ffi::Array<Var> vars, PrimExpr pred) { return 
LambdaExpr(vars, pred); });
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("tirx.LambdaExprApply", [](LambdaExpr pred, 
ffi::Array<PrimExpr> indices) {
-    return pred->Apply(indices);
-  });
-}
-
-}  // namespace tirx
-}  // namespace tvm
diff --git a/src/tirx/ir/tir_visitor_with_path.cc 
b/src/tirx/ir/tir_visitor_with_path.cc
index 77aa2e78ab..c0595a1168 100644
--- a/src/tirx/ir/tir_visitor_with_path.cc
+++ b/src/tirx/ir/tir_visitor_with_path.cc
@@ -314,7 +314,7 @@ void TIRVisitorWithPath::VisitLambda(const LambdaExprNode* 
op, AccessPath path)
   for (size_t i = 0; i < op->vars.size(); ++i) {
     context.push_back(WithDef(op->vars[i], path->Attr("vars")->ArrayItem(i)));
   }
-  Visit(op->pred, path->Attr("pred"));
+  Visit(op->body, path->Attr("body"));
 }
 
 void TIRVisitorWithPath::Dispatch_(const TupleNode* op, AccessPath path) {
diff --git a/src/tirx/op/builtin.cc b/src/tirx/op/builtin.cc
index 3309f9bded..1974e79cc0 100644
--- a/src/tirx/op/builtin.cc
+++ b/src/tirx/op/builtin.cc
@@ -23,6 +23,7 @@
  *  builtin intrinsic operators.
  */
 #include <tvm/ffi/function.h>
+#include <tvm/ir/expr.h>
 #include <tvm/ir/prim/builtin.h>
 #include <tvm/tirx/attrs.h>
 #include <tvm/tirx/builtin.h>
@@ -594,7 +595,13 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       .set_attr<TCallEffectKind>("TCallEffectKind", 
static_cast<int64_t>(CallEffectKind::kOpaque));
 
   OpDef("tirx.tvm_thread_allreduce")
-      .signature(sig::arg<IntExpr>("size", "The size."), sig::var_args("args"))
+      .signature(sig::arg<LambdaExpr>("combine", "The typed combining 
lambda."),
+                 sig::arg<Expr>("identity", "The identity values."),
+                 sig::arg<Expr>("values", "The reduction values."),
+                 sig::arg<PrimExpr>("predicate", "Whether this thread 
contributes."),
+                 sig::arg<Expr>("destinations", "The destination tensor 
loads."),
+                 sig::arg<Expr>("thread_axes", "The reduction thread axes."))
+      .set_attr<TFixedReturnType>("TFixedReturnType", PrimType::Void())
       .set_attr<TScriptPrinterName>("TScriptPrinterName", 
ffi::String("tirx.tvm_thread_allreduce"))
       .set_attr<TIRxOpCategory>("TIRxOpCategory", ffi::String("builtin"))
       .set_attr<TCallEffectKind>("TCallEffectKind", 
static_cast<int64_t>(CallEffectKind::kOpaque));
diff --git a/src/tirx/script/printer/expr.cc b/src/tirx/script/printer/expr.cc
index f92676e1ea..47b713c7a6 100644
--- a/src/tirx/script/printer/expr.cc
+++ b/src/tirx/script/printer/expr.cc
@@ -169,20 +169,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       kDocTranslate, FDocTranslate::FromNative<&IndexMapDocTranslate>());
 }
 
-ffi::Optional<ExprDoc> LambdaExprDocTranslate(DocTranslatorObj* d, 
ffi::AnyView input,
-                                              const ffi::Object*) {
-  const auto* lambda =
-      ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
tirx::LambdaExprNode>(input);
-  ffi::Array<IdDoc> args;
-  for (const Var& var : lambda->vars) args.push_back(VarDoc(d, var));
-  return LambdaDoc(args, d->Translate(lambda->pred).value());
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  ffi::reflection::TypeAttrDef<tirx::LambdaExprNode>().attr(
-      kDocTranslate, FDocTranslate::FromNative<&LambdaExprDocTranslate>());
-}
-
 ffi::Optional<ExprDoc> StorageSyncDocTranslate(DocTranslatorObj* d, 
ffi::AnyView input,
                                                const ffi::Object*) {
   const auto* call =
diff --git a/src/tirx/transform/lower_thread_allreduce.h 
b/src/tirx/transform/lower_thread_allreduce.h
index 81507ae1eb..4e03b7d92a 100644
--- a/src/tirx/transform/lower_thread_allreduce.h
+++ b/src/tirx/transform/lower_thread_allreduce.h
@@ -22,11 +22,11 @@
 
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
+#include <tvm/ir/expr.h>
 #include <tvm/ir/prim/builtin.h>
 #include <tvm/ir/prim/expr.h>
 #include <tvm/sym/analyzer.h>
 #include <tvm/target/target.h>
-#include <tvm/te/operation.h>
 #include <tvm/tirx/builtin.h>
 #include <tvm/tirx/stmt_functor.h>
 #include <tvm/tirx/transform.h>
@@ -83,18 +83,6 @@ class ThreadAllreduceBuilder final : public DialectMutator {
     return result;
   }
 
-  UnchangedOr<Stmt> Mutate_(const AttrStmtNode* op, InplaceMode inplace_mode) 
final {
-    if (op->attr_key == "reduce_scope") {
-      const te::CommReducerNode* combiner = op->node.as<te::CommReducerNode>();
-      TVM_FFI_ICHECK(combiner);
-      reduce_combiner_.push_back(combiner);
-      Stmt ret = DialectMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
-      reduce_combiner_.pop_back();
-      return ret;
-    } else {
-      return DialectMutator::Mutate_(op, inplace_mode);
-    }
-  }
   UnchangedOr<Stmt> Mutate_(const EvaluateNode* op, InplaceMode inplace_mode) 
final {
     Stmt stmt = DialectMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
     op = stmt.as<EvaluateNode>();
@@ -225,32 +213,41 @@ class ThreadAllreduceBuilder final : public 
DialectMutator {
     }
   };
 
+  static ffi::Array<PrimExpr> ApplyCombiner(const LambdaExpr& combiner,
+                                            const ffi::Array<PrimExpr>& lhs,
+                                            const ffi::Array<PrimExpr>& rhs) {
+    ffi::Array<Expr> arguments;
+    for (const PrimExpr& value : lhs) arguments.push_back(value);
+    for (const PrimExpr& value : rhs) arguments.push_back(value);
+    return 
builtin::GetAllreduceFields(combiner->Apply(arguments)).Map([](const Expr& 
value) {
+      return value.as_or_throw<PrimExpr>();
+    });
+  }
+
   // make allreduce.
   Stmt MakeAllreduce(const CallNode* call) {
-    TVM_FFI_ICHECK(!reduce_combiner_.empty());
-    const te::CommReducerNode* combiner = reduce_combiner_.back();
-    size_t size = combiner->result.size();
-
-    const IntImmNode* size_of_args = call->args[0].as<IntImmNode>();
-    TVM_FFI_ICHECK(size_of_args) << call->args[0]->GetTypeKey();
-    TVM_FFI_ICHECK_EQ(size, size_of_args->value);
-    ffi::Array<PrimExpr> inits = combiner->identity_element;
+    LambdaExpr combiner = call->args[0].as_or_throw<LambdaExpr>();
+    ffi::Array<Expr> inits = builtin::GetAllreduceFields(call->args[1]);
+    ffi::Array<Expr> inputs = builtin::GetAllreduceFields(call->args[2]);
+    ffi::Array<Expr> destinations = builtin::GetAllreduceFields(call->args[4]);
+    ffi::Array<Expr> thread_axes = builtin::GetAllreduceFields(call->args[5]);
+    size_t size = inputs.size();
     std::vector<PrimExpr> values;
     values.reserve(size);
     std::vector<PrimType> dtypes;
     dtypes.reserve(size);
-    PrimExpr cond = call->args[size + 1].as_or_throw<PrimExpr>();
+    PrimExpr cond = call->args[3].as_or_throw<PrimExpr>();
     for (size_t idx = 0; idx < size; ++idx) {
-      values.push_back(call->args[1 + idx].as_or_throw<PrimExpr>());
+      values.push_back(inputs[idx].as_or_throw<PrimExpr>());
       if (!is_one(cond)) {
-        values[idx] = Select(cond, values[idx], inits[idx]);
+        values[idx] = Select(cond, values[idx], 
inits[idx].as_or_throw<PrimExpr>());
       }
       dtypes.push_back(values[idx].ty());
     }
     std::vector<TensorVar> buffers;
     buffers.reserve(size);
     for (size_t idx = 0; idx < size; ++idx) {
-      PrimExpr arg = call->args[2 + size + idx].as_or_throw<PrimExpr>();
+      PrimExpr arg = destinations[idx].as_or_throw<PrimExpr>();
       // Loads from boolean buffers may have cast nodes inserted by
       // earlier passes.
       if (auto cast = arg.as<CastNode>()) {
@@ -260,8 +257,8 @@ class ThreadAllreduceBuilder final : public DialectMutator {
     }
 
     std::unordered_set<const VarNode*> reduce_set;
-    for (size_t i = 2 + 2 * size; i < call->args.size(); ++i) {
-      auto var = call->args[i].as<PrimVar>();
+    for (const Expr& axis : thread_axes) {
+      auto var = axis.as<PrimVar>();
       const VarNode* v = var.has_value() ? var.value().get() : nullptr;
       // The simply optimization replace a iteration variable with a constant
       // when extent of the iteration is 1. As threaded IterVar always started 
from 0,
@@ -269,14 +266,14 @@ class ThreadAllreduceBuilder final : public 
DialectMutator {
       if (v) {
         reduce_set.insert(v);
       } else {
-        TVM_FFI_ICHECK(call->args[i].as<IntImmNode>() && 
call->args[i].as<IntImmNode>()->value == 0)
-            << "arg" << i << "should be a VarNode or IntImmNode";
+        TVM_FFI_ICHECK(axis.as<IntImmNode>() && axis.as<IntImmNode>()->value 
== 0)
+            << "Reduction thread axis should be a VarNode or zero IntImmNode";
       }
     }
 
     size_t nmatch = 0;
     std::vector<ThreadEntry> vred, vpar;
-    std::map<int, std::pair<ThreadEntry, bool>> thread_axes;
+    std::map<int, std::pair<ThreadEntry, bool>> thread_axes_by_dim;
     for (const RegionStmtNode* launch : thread_extents_) {
       ThreadEntry e;
       IterVar iv(Range(), launch->body_params[0].as_or_throw<PrimVar>(), 
IterVarType::kThreadIndex,
@@ -291,7 +288,8 @@ class ThreadAllreduceBuilder final : public DialectMutator {
         e.extent = ptr->value.as<int>().value();
         bool is_reduce = reduce_set.count(iv->var.get());
         nmatch += is_reduce;
-        auto [it, inserted] = thread_axes.emplace(e.scope.dim_index, 
std::make_pair(e, is_reduce));
+        auto [it, inserted] =
+            thread_axes_by_dim.emplace(e.scope.dim_index, std::make_pair(e, 
is_reduce));
         if (!inserted) {
           TVM_FFI_ICHECK_EQ(it->second.first.extent, e.extent)
               << "Incompatible extents for nested bindings of " << 
iv->thread_tag;
@@ -303,7 +301,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
         }
       }
     }
-    for (const auto& [dim, entry] : thread_axes) {
+    for (const auto& [dim, entry] : thread_axes_by_dim) {
       if (entry.first.extent != 1) (entry.second ? vred : 
vpar).push_back(entry.first);
     }
     TVM_FFI_ICHECK_EQ(nmatch, reduce_set.size())
@@ -537,7 +535,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
   std::pair<std::vector<PrimExpr>, std::vector<TensorVar>> MakeWarpAllreduce(
       std::vector<PrimExpr> src_values,                  //
       std::vector<PrimType> dtypes,                      //
-      const te::CommReducerNode* combiner,               //
+      const LambdaExpr& combiner,                        //
       PrimExpr reduce_index, int reduce_extent,          //
       PrimExpr group_index,                              //
       PrimExpr mask, ffi::Optional<PrimExpr> predicate,  //
@@ -622,7 +620,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
       }
 
       // Do reductions.
-      ffi::Array<PrimExpr> ret = (*combiner)(a, b);
+      ffi::Array<PrimExpr> ret = ApplyCombiner(combiner, a, b);
 
       // Store the reduction result to itself.
       std::vector<Stmt> stores;
@@ -656,7 +654,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
   }
 
   // make allreduce.
-  Stmt MakeBufAllreduce(const te::CommReducerNode* combiner, const 
std::vector<PrimType>& dtypes,
+  Stmt MakeBufAllreduce(const LambdaExpr& combiner, const 
std::vector<PrimType>& dtypes,
                         const ffi::Array<TensorVar>& shared_bufs, PrimExpr 
reduce_index,
                         PrimExpr group_index, int reduce_extent, int 
group_extent,
                         int contiguous_reduce_extent) {
@@ -683,7 +681,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
         TVM_FFI_ICHECK_EQ(a_load.ty(), dtypes[i]);
         a.push_back(a_load);
       }
-      ffi::Array<PrimExpr> ret = (*combiner)(a, b);
+      ffi::Array<PrimExpr> ret = ApplyCombiner(combiner, a, b);
       return ret;
     };
     auto fstore = [&](const ffi::Array<PrimExpr>& ret) {
@@ -904,7 +902,6 @@ class ThreadAllreduceBuilder final : public DialectMutator {
 
   // surrounding scope of thread extent.
   std::vector<const RegionStmtNode*> thread_extents_;
-  std::vector<const te::CommReducerNode*> reduce_combiner_;
   // The load remap
   std::unordered_map<const VarNode*, PrimExpr> load_remap_;
   // Internal analyzer
diff --git a/src/tirx/transform/unsupported_dtype_legalize.cc 
b/src/tirx/transform/unsupported_dtype_legalize.cc
index bc63f55163..a629658458 100644
--- a/src/tirx/transform/unsupported_dtype_legalize.cc
+++ b/src/tirx/transform/unsupported_dtype_legalize.cc
@@ -24,8 +24,8 @@
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
+#include <tvm/ir/expr.h>
 #include <tvm/ir/prim/builtin.h>
-#include <tvm/te/operation.h>
 #include <tvm/tirx/builtin.h>
 #include <tvm/tirx/op.h>
 #include <tvm/tirx/stmt_functor.h>
@@ -144,7 +144,7 @@ class FP8ComputeLegalizePlanner : public 
ComputeLegalizePlanner {
     PrimExpr origin_b =                                                        
     \
         PromoteToTarget(this->Mutate(op->b, 
inplace_mode).ValueOrUnchanged(op->b)); \
                                                                                
     \
-    if (origin_a.same_as(op->a) && origin_b.same_as(op->b)) {                  
     \
+    if (origin_a.same_as(op->a) && origin_b.same_as(op->b) && 
!MatchType(op->ty)) { \
       return ffi::Unchanged();                                                 
     \
     } else {                                                                   
     \
       return FUNC(origin_a, origin_b);                                         
     \
@@ -201,7 +201,7 @@ class ComputeLegalizer : public StmtExprMutator {
     PrimExpr false_value = PromoteToTarget(
         this->Mutate(op->false_value, 
inplace_mode).ValueOrUnchanged(op->false_value));
     if (condition_unchanged && true_value.same_as(op->true_value) &&
-        false_value.same_as(op->false_value)) {
+        false_value.same_as(op->false_value) && !MatchType(op->ty)) {
       return ffi::Unchanged();
     } else {
       return prim::Select(condition, true_value, false_value);
@@ -211,7 +211,7 @@ class ComputeLegalizer : public StmtExprMutator {
   UnchangedOr<PrimExpr> Mutate_(const prim::BroadcastNode* op, InplaceMode 
inplace_mode) final {
     PrimExpr value =
         PromoteToTarget(this->Mutate(op->value, 
inplace_mode).ValueOrUnchanged(op->value));
-    if (value.same_as(op->value)) {
+    if (value.same_as(op->value) && !MatchType(op->ty)) {
       return ffi::Unchanged();
     } else {
       return prim::Broadcast(value, op->lanes);
@@ -222,7 +222,7 @@ class ComputeLegalizer : public StmtExprMutator {
     auto vectors = op->vectors.Map([this](const PrimExpr& value) {
       return PromoteToTarget(Mutate(value).ValueOrUnchanged(value));
     });
-    if (vectors.same_as(op->vectors)) {
+    if (vectors.same_as(op->vectors) && !MatchType(op->ty)) {
       return ffi::Unchanged();
     } else {
       return prim::Shuffle(vectors, op->indices);
@@ -230,6 +230,9 @@ class ComputeLegalizer : public StmtExprMutator {
   }
 
   UnchangedOr<Expr> Mutate_(const CallNode* op, InplaceMode inplace_mode) 
final {
+    if (op->op.same_as(builtin::tvm_thread_allreduce())) {
+      return LegalizeThreadAllreduce(op);
+    }
     if (op->op.same_as(builtin::alloc_tensor()) || 
op->op.same_as(builtin::decl_tensor())) {
       Call call = StmtExprMutator::Mutate_(op, inplace_mode)
                       .ValueOrUnchanged(ffi::GetRef<Expr>(op))
@@ -403,40 +406,6 @@ class ComputeLegalizer : public StmtExprMutator {
       if (mapped != nullptr) {
         return AttrStmt(mapped.as_or_throw<Var>(), op->attr_key, op->value, 
op->body);
       }
-    } else if (auto reducer = op->node.as<te::CommReducerNode>()) {
-      auto reducer_mode = op->unique() && reducer->unique() ? inplace_mode : 
InplaceMode::kDisallow;
-      auto legalized_identity_elements = Mutate(reducer->identity_element, 
reducer_mode)
-                                             
.as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>()
-                                             
.ValueOrUnchanged(reducer->identity_element);
-
-      // Remap input variables
-      for (size_t i = 0; i < legalized_identity_elements.size(); i++) {
-        Var lhs_var = reducer->lhs[i];
-        if (lhs_var->ty.as_or_throw<PrimType>() != 
legalized_identity_elements[i].ty()) {
-          VarRemapSet(lhs_var, 
lhs_var.CopyWithDType(legalized_identity_elements[i].ty()));
-        }
-        Var rhs_var = reducer->rhs[i];
-        if (rhs_var->ty.as_or_throw<PrimType>() != 
legalized_identity_elements[i].ty()) {
-          VarRemapSet(rhs_var, 
rhs_var.CopyWithDType(legalized_identity_elements[i].ty()));
-        }
-      }
-
-      auto legalized_results = Mutate(reducer->result, reducer_mode)
-                                   
.as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>()
-                                   .ValueOrUnchanged(reducer->result);
-
-      auto legalized_lhs = reducer->lhs.Map([this](PrimVar var) {
-        auto mapped = VarRemapGet(var);
-        return mapped == nullptr ? var : mapped.as_or_throw<PrimVar>();
-      });
-
-      auto legalized_rhs = reducer->rhs.Map([this](PrimVar var) {
-        auto mapped = VarRemapGet(var);
-        return mapped == nullptr ? var : mapped.as_or_throw<PrimVar>();
-      });
-      return AttrStmt(te::CommReducer(legalized_lhs, legalized_rhs, 
legalized_results,
-                                      legalized_identity_elements, 
reducer->span),
-                      op->attr_key, op->value, op->body);
     }
     return ret;
   }
@@ -453,6 +422,40 @@ class ComputeLegalizer : public StmtExprMutator {
   }
 
  private:
+  // Preserve scalar operands and explicit Tuple grouping while promoting 
computation.
+  Expr LegalizeThreadAllreduce(const CallNode* op) {
+    LambdaExpr combine = op->args[0].as_or_throw<LambdaExpr>();
+    auto map_operand = [](const Expr& operand, const auto& transform) -> Expr {
+      if (const auto* tuple = operand.as<tvm::TupleNode>()) {
+        return tvm::Tuple(tuple->fields.Map(transform), operand->span);
+      }
+      return transform(operand);
+    };
+    auto promote = [this](const Expr& value) -> Expr {
+      return 
PromoteToTarget(Mutate(value).ValueOrUnchanged(value).as_or_throw<PrimExpr>());
+    };
+    Expr identity = map_operand(op->args[1], promote);
+    Expr values = map_operand(op->args[2], promote);
+    ffi::Array<Expr> value_fields = builtin::GetAllreduceFields(values);
+    ffi::Array<Var> vars;
+    ffi::Array<Expr> arguments;
+    for (size_t i = 0; i < combine->vars.size(); ++i) {
+      Var promoted = combine->vars[i].CopyWithDType(
+          value_fields[i % value_fields.size()]->ty.as_or_throw<PrimType>());
+      vars.push_back(promoted);
+      arguments.push_back(promoted);
+    }
+    LambdaExpr legalized_combine(vars, map_operand(combine->Apply(arguments), 
promote));
+    auto mutate = [this](const Expr& value) { return 
Mutate(value).ValueOrUnchanged(value); };
+    Expr predicate = mutate(op->args[3]);
+    // Destinations are lvalues: remap promoted allocations without adding 
compute casts.
+    Expr destinations = map_operand(op->args[4], mutate);
+    Expr axes = map_operand(op->args[5], mutate);
+    return Call(PrimType::Void(), op->op,
+                {legalized_combine, identity, values, predicate, destinations, 
axes}, op->attrs,
+                op->ty_args, op->span);
+  }
+
   /*!
    * \brief promote value to target datatype F16/F32 and keep other values 
unchanged.
    * \param value The input value.
diff --git a/tests/python/codegen/test_gpu_codegen_allreduce.py 
b/tests/python/codegen/test_gpu_codegen_allreduce.py
index bbbbf62e57..18f8f9d314 100644
--- a/tests/python/codegen/test_gpu_codegen_allreduce.py
+++ b/tests/python/codegen/test_gpu_codegen_allreduce.py
@@ -27,9 +27,9 @@ from tvm.testing import env
 
 
 def _reduce_module(d1, d2, d3, is_max=False):
-    reducer = T.comm_reducer(
-        (lambda x, y: T.max(x, y)) if is_max else (lambda x, y: x + y),
-        [T.float32(-3.4028234663852886e38 if is_max else 0)],
+    combine = T.Lambda(
+        [T.float32, T.float32],
+        (lambda x, y: (T.max(x, y),)) if is_max else (lambda x, y: (x + y,)),
     )
 
     @I.ir_module
@@ -41,10 +41,14 @@ def _reduce_module(d1, d2, d3, is_max=False):
                     for k in T.thread_binding(d2, thread="threadIdx.y"):
                         for l in T.thread_binding(d3, thread="threadIdx.x"):
                             reduced = T.alloc_tensor((1,), "float32", 
scope="local")
-                            with T.attr(reducer, "reduce_scope", 0):
-                                T.tvm_thread_allreduce(
-                                    T.uint32(1), A[i, j, k, l], True, 
reduced[0], l
-                                )
+                            T.tvm_thread_allreduce(
+                                combine,
+                                (T.float32(-3.4028234663852886e38 if is_max 
else 0),),
+                                (A[i, j, k, l],),
+                                True,
+                                (reduced[0],),
+                                (l,),
+                            )
                             if l == 0:
                                 B[i, j, k] = reduced[0]
 
diff --git a/tests/python/codegen/test_target_codegen_cuda.py 
b/tests/python/codegen/test_target_codegen_cuda.py
index 997798ae39..8b33848e71 100644
--- a/tests/python/codegen/test_target_codegen_cuda.py
+++ b/tests/python/codegen/test_target_codegen_cuda.py
@@ -533,10 +533,14 @@ def test_crossthread_reduction1(target):
                         for m_1 in range((m + nthd - 1) // nthd):
                             if m_0 * ((m + nthd - 1) // nthd) + m_1 < m:
                                 partial[0] = partial[0] + A[i, m_0 * ((m + 
nthd - 1) // nthd) + m_1]
-                        with T.attr(
-                            T.comm_reducer(lambda x, y: x + y, 
[T.float32(0)]), "reduce_scope", 0
-                        ):
-                            T.tvm_thread_allreduce(T.uint32(1), partial[0], 
True, reduced[0], m_0)
+                        T.tvm_thread_allreduce(
+                            T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                            (T.float32(0),),
+                            (partial[0],),
+                            True,
+                            (reduced[0],),
+                            (m_0,),
+                        )
                         if m_0 == 0:
                             B[i] = reduced[0]
 
@@ -608,14 +612,14 @@ def test_crossthread_reduction2(target):
                                             k1_0 * ((k1 + nthdy - 1) // nthdy) 
+ k1_1,
                                         ]
                                     )
-                            with T.attr(
-                                T.comm_reducer(lambda x, y: x + y, 
[T.float32(0)]),
-                                "reduce_scope",
-                                0,
-                            ):
-                                T.tvm_thread_allreduce(
-                                    T.uint32(1), partial[0], True, reduced[0], 
k0_0, k1_0
-                                )
+                            T.tvm_thread_allreduce(
+                                T.Lambda([T.float32, T.float32], lambda x, y: 
(x + y,)),
+                                (T.float32(0),),
+                                (partial[0],),
+                                True,
+                                (reduced[0],),
+                                (k0_0, k1_0),
+                            )
                             if k0_0 == 0 and k1_0 == 0:
                                 B[i] = reduced[0]
 
diff --git a/tests/python/codegen/test_target_codegen_cuda_fp8.py 
b/tests/python/codegen/test_target_codegen_cuda_fp8.py
index ec330668f8..ff53b524bc 100644
--- a/tests/python/codegen/test_target_codegen_cuda_fp8.py
+++ b/tests/python/codegen/test_target_codegen_cuda_fp8.py
@@ -574,12 +574,14 @@ def test_moe_gemv_shfl_down_illegal_instr():
                                 )
                                 * T.Cast("float16", scale[0])
                             )
-                        with T.attr(
-                            T.comm_reducer(lambda x, y: x + y, 
[T.float16(0)]), "reduce_scope", 0
-                        ):
-                            T.tvm_thread_allreduce(
-                                T.uint32(1), partial[0], True, reduced[0], 
reduction
-                            )
+                        T.tvm_thread_allreduce(
+                            T.Lambda([T.float16, T.float16], lambda x, y: (x + 
y,)),
+                            (T.float16(0),),
+                            (partial[0],),
+                            True,
+                            (reduced[0],),
+                            (reduction,),
+                        )
                         if reduction == 0:
                             output[expert, block * 4 + spatial] = reduced[0]
 
diff --git a/tests/python/s_tir/script/test_s_tir_script_printer.py 
b/tests/python/s_tir/script/test_s_tir_script_printer.py
index 5ab098fdfe..e3787402c5 100644
--- a/tests/python/s_tir/script/test_s_tir_script_printer.py
+++ b/tests/python/s_tir/script/test_s_tir_script_printer.py
@@ -1318,18 +1318,16 @@ def comm_reducer_single_reduce_group():
         for i in T.serial(0, 128):
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
             reduce_temp0 = T.alloc_tensor((1,), scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x, y: x + y, [T.float32(0)]), 
"reduce_scope", T.int32(0)
-            ):
-                T.evaluate(
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        A[i * 128 + threadIdx_x],
-                        True,
-                        reduce_temp0.data,
-                        threadIdx_x,
-                    )
+            T.evaluate(
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x, y: (x + y,)),
+                    (T.float32(0),),
+                    (A[i * 128 + threadIdx_x],),
+                    True,
+                    (reduce_temp0[0],),
+                    (threadIdx_x,),
                 )
+            )
 
     return comm_reducer_single_reduce_group
 
@@ -1343,27 +1341,24 @@ def comm_reducer_multiple_reduce_groups():
 
         for i in T.serial(0, 128):
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
-            reduce_temp0 = T.alloc_tensor((1,), scope="local")
-            with T.attr(
-                T.comm_reducer(
-                    lambda x0, x1, y0, y1: (
-                        T.Select((x1 >= y1), x0, y0),
-                        T.Select((x1 >= y1), x1, y1),
+            reduce_temp0 = T.alloc_tensor((1,), "int32", scope="local")
+            reduce_temp1 = T.alloc_tensor((1,), "float32", scope="local")
+            T.evaluate(
+                T.tvm_thread_allreduce(
+                    T.Lambda(
+                        [T.int32, T.float32, T.int32, T.float32],
+                        lambda x0, x1, y0, y1: (
+                            T.Select(x1 >= y1, x0, y0),
+                            T.Select(x1 >= y1, x1, y1),
+                        ),
                     ),
-                    [T.int32(-1), T.min_value("float32")],
-                ),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                T.evaluate(
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        A[i * 128 + threadIdx_x],
-                        True,
-                        reduce_temp0.data,
-                        threadIdx_x,
-                    )
+                    (T.int32(-1), T.min_value("float32")),
+                    (i * 128 + threadIdx_x, A[i * 128 + threadIdx_x]),
+                    True,
+                    (reduce_temp0[0], reduce_temp1[0]),
+                    (threadIdx_x,),
                 )
+            )
 
     return comm_reducer_multiple_reduce_groups
 
@@ -1386,32 +1381,26 @@ def multiple_commreducer():
         )
         for ax0_1 in T.thread_binding(0, 32, thread="threadIdx.x"):
             with Ts.sblock("T_softmax_maxelem_cross_thread_reduction"):
-                T.attr(
-                    T.comm_reducer(lambda x, y: T.max(x, y), 
[T.min_value("float32")]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp0[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: 
(T.max(x, y),)),
+                        (T.min_value("float32"),),
+                        (normal_reduce_temp0[0],),
                         True,
-                        reduce_temp0.data,
-                        ax0_1,
+                        (reduce_temp0[0],),
+                        (ax0_1,),
                     )
                 )
         for ax0_1 in T.thread_binding(0, 32, thread="threadIdx.x"):
             with Ts.sblock("T_softmax_expsum_cross_thread_reduction"):
-                T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]), 
"reduce_scope", T.int32(0)
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp1[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (normal_reduce_temp1[0],),
                         True,
-                        reduce_temp1.data,
-                        ax0_1,
+                        (reduce_temp1[0],),
+                        (ax0_1,),
                     )
                 )
 
@@ -1884,30 +1873,25 @@ def tvm_shfl_builtins():
             T.tvm_warp_activemask(), A_warp_1[0], threadIdx_x % 4 * 8 + 
threadIdx_x // 4, 32, 32
         ) + T.float32(1)
         red_buf0_1 = T.decl_tensor((1,), data=red_buf0.data, scope="local")
-        with T.attr(
-            T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-            "reduce_scope",
-            T.int32(0),
-        ):
-            mask = T.alloc_tensor((1,), "uint32", scope="local")
-            t0 = T.alloc_tensor((1,), scope="local")
-            red_buf0_1[0] = A_warp_1[0]
-            mask_1 = T.decl_tensor((1,), "uint32", data=mask.data, 
scope="local")
-            mask_1[0] = T.tvm_warp_activemask()
-            t0_1 = T.decl_tensor((1,), data=t0.data, scope="local")
-            t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 16, 
32, 32)
-            red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
-            t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 8, 32, 
32)
-            red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
-            t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 4, 32, 
32)
-            red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
-            t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 2, 32, 
32)
-            red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
-            t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 1, 32, 
32)
-            red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
-            red_buf0_1[0] = T.tvm_warp_shuffle(mask_1[0], red_buf0_1[0], 0, 
32, 32)
-            # NOTE(Zihao): test tvm_warp_shuffle_up
-            red_buf0_1[0] = T.tvm_warp_shuffle_up(mask_1[0], red_buf0_1[0], 0, 
32, 32)
+        mask = T.alloc_tensor((1,), "uint32", scope="local")
+        t0 = T.alloc_tensor((1,), scope="local")
+        red_buf0_1[0] = A_warp_1[0]
+        mask_1 = T.decl_tensor((1,), "uint32", data=mask.data, scope="local")
+        mask_1[0] = T.tvm_warp_activemask()
+        t0_1 = T.decl_tensor((1,), data=t0.data, scope="local")
+        t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 16, 32, 32)
+        red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
+        t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 8, 32, 32)
+        red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
+        t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 4, 32, 32)
+        red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
+        t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 2, 32, 32)
+        red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
+        t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 1, 32, 32)
+        red_buf0_1[0] = red_buf0_1[0] + t0_1[0]
+        red_buf0_1[0] = T.tvm_warp_shuffle(mask_1[0], red_buf0_1[0], 0, 32, 32)
+        # NOTE(Zihao): test tvm_warp_shuffle_up
+        red_buf0_1[0] = T.tvm_warp_shuffle_up(mask_1[0], red_buf0_1[0], 0, 32, 
32)
         if threadIdx_x == 0:
             C_1 = T.decl_tensor((1,), data=C)
             C_1[0] = red_buf0_1[0]
@@ -2322,18 +2306,14 @@ def lowered_loop_split(
             with Ts.sblock("B_cross_thread_reduction"):
                 Ts.reads([normal_reduce_temp0[0]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp0[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (normal_reduce_temp0[0],),
                         True,
-                        reduce_temp0.data,
-                        ki,
+                        (reduce_temp0[0],),
+                        (ki,),
                     )
                 )
             with Ts.sblock("B_write_back"):
diff --git 
a/tests/python/s_tir/transform/test_s_tir_transform_lift_thread_binding.py 
b/tests/python/s_tir/transform/test_s_tir_transform_lift_thread_binding.py
index c15e9c1ce9..fcd1df9d78 100644
--- a/tests/python/s_tir/transform/test_s_tir_transform_lift_thread_binding.py
+++ b/tests/python/s_tir/transform/test_s_tir_transform_lift_thread_binding.py
@@ -64,8 +64,7 @@ def test_lift_tx_beyond_local():
                             with Ts.sblock("NT_matmul_cross_thread"):
                                 Ts.reads(in_thread_D_local[0])
                                 Ts.writes(cross_thread_D_local[0])
-                                T.attr(T.comm_reducer(lambda x0, y0: x0 + y0, 
[T.float32(0)]), "reduce_scope", T.int32(0))
-                                T.tvm_thread_allreduce(T.uint32(1), 
in_thread_D_local[0], T.bool(True), cross_thread_D_local[0], ax0_fused)
+                                T.tvm_thread_allreduce(T.Lambda([T.float32, 
T.float32], lambda x0, y0: (x0 + y0,)), (T.float32(0),), 
(in_thread_D_local[0],), T.bool(True), (cross_thread_D_local[0],), (ax0_fused,))
                             with Ts.sblock("NT_matmul_write_back"):
                                 Ts.where(ax0_fused == 0)
                                 Ts.reads(cross_thread_D_local[0])
@@ -118,8 +117,7 @@ def test_lift_tx_beyond_local():
                             with Ts.sblock("NT_matmul_cross_thread"):
                                 Ts.reads(in_thread_D_local[0])
                                 Ts.writes(cross_thread_D_local[0])
-                                T.attr(T.comm_reducer(lambda x0, y0: x0 + y0, 
[T.float32(0)]), "reduce_scope", T.int32(0))
-                                T.tvm_thread_allreduce(T.uint32(1), 
in_thread_D_local[0], T.bool(True), cross_thread_D_local[0], threadIdx_x)
+                                T.tvm_thread_allreduce(T.Lambda([T.float32, 
T.float32], lambda x0, y0: (x0 + y0,)), (T.float32(0),), 
(in_thread_D_local[0],), T.bool(True), (cross_thread_D_local[0],), 
(threadIdx_x,))
                             with Ts.sblock("NT_matmul_write_back"):
                                 Ts.where(threadIdx_x == 0)
                                 Ts.reads(cross_thread_D_local[0])
diff --git 
a/tests/python/s_tir/transform/test_s_tir_transform_lower_cross_thread_reduction.py
 
b/tests/python/s_tir/transform/test_s_tir_transform_lower_cross_thread_reduction.py
index 5543f3c7d9..43256bb119 100644
--- 
a/tests/python/s_tir/transform/test_s_tir_transform_lower_cross_thread_reduction.py
+++ 
b/tests/python/s_tir/transform/test_s_tir_transform_lower_cross_thread_reduction.py
@@ -81,18 +81,14 @@ def lowered_loop_split(
             with Ts.sblock("B_cross_thread_reduction"):
                 Ts.reads([normal_reduce_temp0[0]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp0[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (normal_reduce_temp0[0],),
                         True,
-                        reduce_temp0[0],
-                        ki,
+                        (reduce_temp0[0],),
+                        (ki,),
                     )
                 )
             with Ts.sblock("B_write_back"):
@@ -130,12 +126,16 @@ def lowered_no_normal_reduction(
                 vi, vk = Ts.axis.remap("SR", [i, k])
                 Ts.reads([A[vi, vk]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
+                T.evaluate(
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (A[vi, vk],),
+                        True,
+                        (reduce_temp0[0],),
+                        (k,),
+                    )
                 )
-                T.evaluate(T.tvm_thread_allreduce(T.uint32(1), A[vi, vk], 
True, reduce_temp0[0], k))
             with Ts.sblock("B_write_back"):
                 vi = Ts.axis.spatial(128, i)
                 Ts.where(k == 0)
@@ -175,14 +175,14 @@ def lowered_two_bound_loops(
                     vk = Ts.axis.reduce(128, ko * 32 + ki)
                     Ts.reads([A[vi, vk]])
                     Ts.writes([reduce_temp0[0]])
-                    T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    )
                     T.evaluate(
                         T.tvm_thread_allreduce(
-                            T.uint32(1), A[vi, vk], True, reduce_temp0[0], ko, 
ki
+                            T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                            (T.float32(0),),
+                            (A[vi, vk],),
+                            True,
+                            (reduce_temp0[0],),
+                            (ko, ki),
                         )
                     )
                 with Ts.sblock("B_write_back"):
@@ -252,18 +252,14 @@ def lowered_multiple_blocks_under_reduction_loop(
             with Ts.sblock("B_cross_thread_reduction"):
                 Ts.reads([normal_reduce_temp0[0]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp0[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (normal_reduce_temp0[0],),
                         True,
-                        reduce_temp0[0],
-                        k0o,
+                        (reduce_temp0[0],),
+                        (k0o,),
                     )
                 )
             with Ts.sblock("B_write_back"):
@@ -314,18 +310,14 @@ def lowered_with_block_predicate(
             with Ts.sblock("B_cross_thread_reduction"):
                 Ts.reads([normal_reduce_temp0[0]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp0[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (normal_reduce_temp0[0],),
                         True,
-                        reduce_temp0[0],
-                        ki,
+                        (reduce_temp0[0],),
+                        (ki,),
                     )
                 )
             with Ts.sblock("B_write_back"):
@@ -414,20 +406,14 @@ def lowered_single_reduction_loop_with_block_predicate(
                 with Ts.sblock("T_softmax_maxelem_cross_thread"):
                     Ts.reads(in_thread_0[0])
                     Ts.writes(cross_thread_0[0])
-                    T.attr(
-                        T.comm_reducer(
-                            lambda x, y: T.max(x, y), 
[T.float32(-3.4028234663852886e38)]
-                        ),
-                        "reduce_scope",
-                        T.int32(0),
-                    )
                     T.evaluate(
                         T.tvm_thread_allreduce(
-                            T.uint32(1),
-                            in_thread_0[0],
+                            T.Lambda([T.float32, T.float32], lambda x, y: 
(T.max(x, y),)),
+                            (T.float32(-3.4028234663852886e38),),
+                            (in_thread_0[0],),
                             True,
-                            cross_thread_0[0],
-                            ax1_1,
+                            (cross_thread_0[0],),
+                            (ax1_1,),
                         )
                     )
                 with Ts.sblock("T_softmax_maxelem_write_back"):
@@ -455,18 +441,14 @@ def lowered_single_reduction_loop_with_block_predicate(
                 with Ts.sblock("T_softmax_expsum_cross_thread"):
                     Ts.reads(in_thread_1[0])
                     Ts.writes(cross_thread_1[0])
-                    T.attr(
-                        T.comm_reducer(lambda x_1, y_1: x_1 + y_1, 
[T.float32(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    )
                     T.evaluate(
                         T.tvm_thread_allreduce(
-                            T.uint32(1),
-                            in_thread_1[0],
+                            T.Lambda([T.float32, T.float32], lambda x_1, y_1: 
(x_1 + y_1,)),
+                            (T.float32(0),),
+                            (in_thread_1[0],),
                             True,
-                            cross_thread_1[0],
-                            ax1_1,
+                            (cross_thread_1[0],),
+                            (ax1_1,),
                         )
                     )
                 with Ts.sblock("T_softmax_expsum_write_back"):
@@ -683,17 +665,13 @@ def lowered_spatial_reduction_with_shared_prefetch(
                 with Ts.sblock("B_cross_thread"):
                     Ts.reads(in_thread_C_local[0])
                     Ts.writes(cross_thread_C_local[0])
-                    T.attr(
-                        T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    )
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        in_thread_C_local[0],
+                        T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + 
y0,)),
+                        (T.float32(0),),
+                        (in_thread_C_local[0],),
                         T.bool(True),
-                        cross_thread_C_local[0],
-                        ax2_1_1_fused,
+                        (cross_thread_C_local[0],),
+                        (ax2_1_1_fused,),
                     )
                 with Ts.sblock("B_write_back"):
                     v0 = Ts.axis.spatial(128, ax0_0_ax1_0_fused // 16 * 8 + 
ax0_1_ax1_1_fused // 8)
@@ -755,13 +733,13 @@ def lowered_reduction_spatial_loop_predicate(
                 with Ts.sblock("block_cross_thread"):
                     Ts.reads(in_thread_B[0])
                     Ts.writes(cross_thread_B[0])
-                    T.attr(
-                        T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    )
                     T.tvm_thread_allreduce(
-                        T.uint32(1), in_thread_B[0], T.bool(True), 
cross_thread_B[0], k_1
+                        T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + 
y0,)),
+                        (T.float32(0),),
+                        (in_thread_B[0],),
+                        T.bool(True),
+                        (cross_thread_B[0],),
+                        (k_1,),
                     )
                 with Ts.sblock("block_write_back"):
                     vi = Ts.axis.spatial(2, i_0 * 16 + i_1)
@@ -913,12 +891,16 @@ def lowered_reducer_max(
                 vi, vk = Ts.axis.remap("SR", [i, k])
                 Ts.reads([A[vi, vk]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: T.max(x, y), 
[T.min_value("float32")]),
-                    "reduce_scope",
-                    T.int32(0),
+                T.evaluate(
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.float32, T.float32], lambda x, y: 
(T.max(x, y),)),
+                        (T.min_value("float32"),),
+                        (A[vi, vk],),
+                        True,
+                        (reduce_temp0[0],),
+                        (k,),
+                    )
                 )
-                T.evaluate(T.tvm_thread_allreduce(T.uint32(1), A[vi, vk], 
True, reduce_temp0[0], k))
             with Ts.sblock("B_write_back"):
                 vi = Ts.axis.spatial(128, i)
                 Ts.where(k == 0)
@@ -950,12 +932,16 @@ def lowered_zero_rank_buffer(
             vk = Ts.axis.reduce(128, k)
             Ts.reads([A[vk]])
             Ts.writes([reduce_temp0[0]])
-            T.attr(
-                T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
+            T.evaluate(
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x, y: (x + y,)),
+                    (T.float32(0),),
+                    (A[vk],),
+                    True,
+                    (reduce_temp0[0],),
+                    (k,),
+                )
             )
-            T.evaluate(T.tvm_thread_allreduce(T.uint32(1), A[vk], True, 
reduce_temp0[0], k))
         with Ts.sblock("B_write_back"):
             Ts.reads([reduce_temp0[0]])
             Ts.writes([B[()]])
@@ -1131,18 +1117,14 @@ def lowered_softmax(
             with Ts.sblock("T_softmax_maxelem_cross_thread_reduction"):
                 Ts.reads([normal_reduce_temp0[0]])
                 Ts.writes([reduce_temp0[0]])
-                T.attr(
-                    T.comm_reducer(lambda x, y: T.max(x, y), 
[T.min_value("float32")]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp0[0],
+                        T.Lambda([T.float32, T.float32], lambda x, y: 
(T.max(x, y),)),
+                        (T.min_value("float32"),),
+                        (normal_reduce_temp0[0],),
                         True,
-                        reduce_temp0[0],
-                        ax0_1,
+                        (reduce_temp0[0],),
+                        (ax0_1,),
                     )
                 )
             with Ts.sblock("T_softmax_maxelem_write_back"):
@@ -1173,18 +1155,14 @@ def lowered_softmax(
             with Ts.sblock("T_softmax_expsum_cross_thread_reduction"):
                 Ts.reads([normal_reduce_temp1[0]])
                 Ts.writes([reduce_temp1[0]])
-                T.attr(
-                    T.comm_reducer(lambda x_1, y_1: x_1 + y_1, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_temp1[0],
+                        T.Lambda([T.float32, T.float32], lambda x_1, y_1: (x_1 
+ y_1,)),
+                        (T.float32(0),),
+                        (normal_reduce_temp1[0],),
                         True,
-                        reduce_temp1[0],
-                        ax0_1,
+                        (reduce_temp1[0],),
+                        (ax0_1,),
                     )
                 )
             with Ts.sblock("T_softmax_expsum_write_back"):
@@ -1279,26 +1257,20 @@ def lowered_argmax_split(
             with Ts.sblock("argmax_cross_thread"):
                 Ts.reads(in_thread_argmax_v0[0], in_thread_argmax_v1[0])
                 Ts.writes(cross_thread_argmax_v0[0], cross_thread_argmax_v1[0])
-                T.attr(
-                    T.comm_reducer(
-                        lambda x0, x1, y0, y1: (
-                            T.Select(x1 >= y1, x0, y0),
-                            T.Select(x1 >= y1, x1, y1),
-                        ),
-                        [-1, T.float32(-3.4028234663852886e38)],
-                    ),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(2),
-                        in_thread_argmax_v0[0],
-                        in_thread_argmax_v1[0],
+                        T.Lambda(
+                            [T.int32, T.float32, T.int32, T.float32],
+                            lambda x0, x1, y0, y1: (
+                                T.Select(x1 >= y1, x0, y0),
+                                T.Select(x1 >= y1, x1, y1),
+                            ),
+                        ),
+                        (-1, T.float32(-3.4028234663852886e38)),
+                        (in_thread_argmax_v0[0], in_thread_argmax_v1[0]),
                         True,
-                        cross_thread_argmax_v0[0],
-                        cross_thread_argmax_v1[0],
-                        i1_1,
+                        (cross_thread_argmax_v0[0], cross_thread_argmax_v1[0]),
+                        (i1_1,),
                     )
                 )
             with Ts.sblock("argmax_write_back"):
@@ -1374,26 +1346,20 @@ def lowered_argmin_split_init_update_reordered(
             with Ts.sblock("argmin_cross_thread"):
                 Ts.reads(in_thread_argmin_v0[0], in_thread_argmin_v1[0])
                 Ts.writes(cross_thread_argmin_v0[0], cross_thread_argmin_v1[0])
-                T.attr(
-                    T.comm_reducer(
-                        lambda x0, x1, y0, y1: (
-                            T.Select(x1 <= y1, x0, y0),
-                            T.Select(x1 <= y1, x1, y1),
-                        ),
-                        [-1, T.float32(3.4028234663852886e38)],
-                    ),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(2),
-                        in_thread_argmin_v0[0],
-                        in_thread_argmin_v1[0],
+                        T.Lambda(
+                            [T.int32, T.float32, T.int32, T.float32],
+                            lambda x0, x1, y0, y1: (
+                                T.Select(x1 <= y1, x0, y0),
+                                T.Select(x1 <= y1, x1, y1),
+                            ),
+                        ),
+                        (-1, T.float32(3.4028234663852886e38)),
+                        (in_thread_argmin_v0[0], in_thread_argmin_v1[0]),
                         True,
-                        cross_thread_argmin_v0[0],
-                        cross_thread_argmin_v1[0],
-                        i1_1,
+                        (cross_thread_argmin_v0[0], cross_thread_argmin_v1[0]),
+                        (i1_1,),
                     )
                 )
             with Ts.sblock("argmin_write_back"):
@@ -1501,22 +1467,17 @@ def lowered_layer_norm_tuple_sum(
             with Ts.sblock("data_red_temp_cross_thread"):
                 Ts.reads(in_thread_data_red_temp_v0[0], 
in_thread_data_red_temp_v1[0])
                 Ts.writes(cross_thread_data_red_temp_v0[0], 
cross_thread_data_red_temp_v1[0])
-                T.attr(
-                    T.comm_reducer(
-                        lambda x0, x1, y0, y1: (x0 + y0, x1 + y1), 
[T.float32(0), T.float32(0)]
-                    ),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.evaluate(
                     T.tvm_thread_allreduce(
-                        T.uint32(2),
-                        in_thread_data_red_temp_v0[0],
-                        in_thread_data_red_temp_v1[0],
+                        T.Lambda(
+                            [T.float32, T.float32, T.float32, T.float32],
+                            lambda x0, x1, y0, y1: (x0 + y0, x1 + y1),
+                        ),
+                        (T.float32(0), T.float32(0)),
+                        (in_thread_data_red_temp_v0[0], 
in_thread_data_red_temp_v1[0]),
                         True,
-                        cross_thread_data_red_temp_v0[0],
-                        cross_thread_data_red_temp_v1[0],
-                        i1_1,
+                        (cross_thread_data_red_temp_v0[0], 
cross_thread_data_red_temp_v1[0]),
+                        (i1_1,),
                     )
                 )
             with Ts.sblock("data_red_temp_write_back"):
@@ -1580,13 +1541,13 @@ def lowered_thread_broadcast_1(A: T.Tensor((256, 256), 
"float32"), B: T.Tensor((
                 vi, vk = Ts.axis.remap("SR", [i, k])
                 Ts.reads(A[vi, vk])
                 Ts.writes(cross_thread_temp_local[0])
-                T.attr(
-                    T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.tvm_thread_allreduce(
-                    T.uint32(1), A[vi, vk], T.bool(True), 
cross_thread_temp_local[0], k
+                    T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + 
y0,)),
+                    (T.float32(0),),
+                    (A[vi, vk],),
+                    T.bool(True),
+                    (cross_thread_temp_local[0],),
+                    (k,),
                 )
             with Ts.sblock("sum_write_back"):
                 vi = Ts.axis.spatial(256, i)
@@ -1694,8 +1655,7 @@ def lowered_thread_broadcast_2(lv1605: 
T.Tensor((T.int64(1), T.int64(32), T.int6
                 with Ts.sblock("NT_matmul_cross_thread"):
                     Ts.reads(in_thread_var_NT_matmul_intermediate_local[0])
                     Ts.writes(cross_thread_var_NT_matmul_intermediate_local[0])
-                    T.attr(T.comm_reducer(lambda x0, y0: x0 + y0, 
[T.float16(0)]), "reduce_scope", T.int32(0))
-                    T.tvm_thread_allreduce(T.uint32(1), 
in_thread_var_NT_matmul_intermediate_local[0], T.bool(True), 
cross_thread_var_NT_matmul_intermediate_local[0], ax0_fused)
+                    T.tvm_thread_allreduce(T.Lambda([T.float16, T.float16], 
lambda x0, y0: (x0 + y0,)), (T.float16(0),), 
(in_thread_var_NT_matmul_intermediate_local[0],), T.bool(True), 
(cross_thread_var_NT_matmul_intermediate_local[0],), (ax0_fused,))
                 with Ts.sblock("NT_matmul_write_back"):
                     v0 = Ts.axis.spatial(T.int64(32), ax0_ax1_fused // n)
                     v1 = Ts.axis.spatial(n, ax0_ax1_fused % n)
@@ -1754,13 +1714,13 @@ def lowered_no_thread_broadcast(
                 vi, vk = Ts.axis.remap("SR", [i, k])
                 Ts.reads(A[vi, vk])
                 Ts.writes(cross_thread_temp_1_local[0])
-                T.attr(
-                    T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                )
                 T.tvm_thread_allreduce(
-                    T.uint32(1), A[vi, vk], T.bool(True), 
cross_thread_temp_1_local[0], k
+                    T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + 
y0,)),
+                    (T.float32(0),),
+                    (A[vi, vk],),
+                    T.bool(True),
+                    (cross_thread_temp_1_local[0],),
+                    (k,),
                 )
             with Ts.sblock("sum_write_back"):
                 vi = Ts.axis.spatial(256, i)
diff --git 
a/tests/python/s_tir/transform/test_s_tir_transform_lower_thread_all_reduce.py 
b/tests/python/s_tir/transform/test_s_tir_transform_lower_thread_all_reduce.py
index 0ab0f63c26..7be9050194 100644
--- 
a/tests/python/s_tir/transform/test_s_tir_transform_lower_thread_all_reduce.py
+++ 
b/tests/python/s_tir/transform/test_s_tir_transform_lower_thread_all_reduce.py
@@ -63,18 +63,14 @@ def test_basic():
                 reduce = T.alloc_tensor((1,), scope="local")
                 reduce_1 = T.decl_tensor(1, data=reduce.data, scope="local")
 
-                with T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                ):
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        A_flat[0],
-                        T.bool(True),
-                        reduce_1[0],
-                        threadIdx_x,
-                    )
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x, y: x + y),
+                    T.float32(0),
+                    A_flat[0],
+                    T.bool(True),
+                    reduce_1[0],
+                    threadIdx_x,
+                )
                 if threadIdx_x == 0:
                     B[i] = reduce_1[0]
 
@@ -102,18 +98,14 @@ def test_basic_with_decl_buffer():
 
                 reduce = T.decl_tensor(1, dtype="float32", scope="local")
 
-                with T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                ):
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        A_flat[0],
-                        T.bool(True),
-                        reduce[0],
-                        threadIdx_x,
-                    )
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x, y: (x + y,)),
+                    (T.float32(0),),
+                    (A_flat[0],),
+                    T.bool(True),
+                    (reduce[0],),
+                    (threadIdx_x,),
+                )
                 if threadIdx_x == 0:
                     B[i] = reduce[0]
 
@@ -150,18 +142,14 @@ def test_reduce_summation():
                         normal_reduce_1[0] + A_flat[i * 128 + ko * 32 + 
threadIdx_x]
                     )
 
-                with T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                ):
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        normal_reduce_1[0],
-                        T.bool(True),
-                        reduce_1[0],
-                        threadIdx_x,
-                    )
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x, y: (x + y,)),
+                    (T.float32(0),),
+                    (normal_reduce_1[0],),
+                    T.bool(True),
+                    (reduce_1[0],),
+                    (threadIdx_x,),
+                )
                 if threadIdx_x == 0:
                     B[i] = reduce_1[0]
 
@@ -183,19 +171,15 @@ def test_multi_group_reduction():
             cross_thread_B = T.alloc_tensor((1,), scope="local")
             threadIdx_x = T.launch_thread("threadIdx.x", 32)
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_1 = T.decl_tensor((1024,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_1[threadIdx_y * 32 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B_1[0],
-                    threadIdx_x,
-                )
+            A_1 = T.decl_tensor((1024,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_1[threadIdx_y * 32 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((32,), data=B.data)
                 B_1[threadIdx_y] = cross_thread_B_1[0]
@@ -218,19 +202,15 @@ def test_multi_group_reduction_consumed_through_alias():
             cross_thread_B = T.alloc_tensor((1,), scope="local")
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
             cross_thread_B_alias = T.decl_tensor((1,), 
data=cross_thread_B.data, scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_flat = T.decl_tensor((512,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_flat[threadIdx_y * 128 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B[0],
-                    threadIdx_x,
-                )
+            A_flat = T.decl_tensor((512,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_flat[threadIdx_y * 128 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B[0],),
+                (threadIdx_x,),
+            )
             cross_thread_B_alias[0] = cross_thread_B[0]
             if threadIdx_x == 0:
                 B_flat = T.decl_tensor((4,), data=B.data)
@@ -257,19 +237,15 @@ def 
test_multi_group_reduction_with_alias_declared_after_allreduce():
             threadIdx_y = T.launch_thread("threadIdx.y", 4)
             cross_thread_B = T.alloc_tensor((1,), scope="local")
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_flat = T.decl_tensor((512,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_flat[threadIdx_y * 128 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B[0],
-                    threadIdx_x,
-                )
+            A_flat = T.decl_tensor((512,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_flat[threadIdx_y * 128 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B[0],),
+                (threadIdx_x,),
+            )
             cross_thread_B_alias = T.decl_tensor((1,), 
data=cross_thread_B.data, scope="local")
             cross_thread_B[0] = cross_thread_B_alias[0]
             if threadIdx_x == 0:
@@ -298,19 +274,15 @@ def test_multi_group_mask1():
             cross_thread_B = T.alloc_tensor((1,), scope="local")
             threadIdx_x = T.launch_thread("threadIdx.x", 8)
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_1 = T.decl_tensor((256,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_1[threadIdx_y * 8 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B_1[0],
-                    threadIdx_x,
-                )
+            A_1 = T.decl_tensor((256,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_1[threadIdx_y * 8 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((32,), data=B.data)
                 B_1[threadIdx_y] = cross_thread_B_1[0]
@@ -333,19 +305,15 @@ def test_multi_warp_reduce1():
                 threadIdx_x = T.launch_thread("threadIdx.x", 128)
                 cross_thread_B = T.alloc_tensor((1,), scope="local")
                 cross_thread_B_1 = T.decl_tensor((1,), 
data=cross_thread_B.data, scope="local")
-                with T.attr(
-                    T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                ):
-                    A_1 = T.decl_tensor((16384,), data=A.data)
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        A_1[i * 128 + threadIdx_x],
-                        T.bool(True),
-                        cross_thread_B_1[0],
-                        threadIdx_x,
-                    )
+                A_1 = T.decl_tensor((16384,), data=A.data)
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + 
y0,)),
+                    (T.float32(0),),
+                    (A_1[i * 128 + threadIdx_x],),
+                    T.bool(True),
+                    (cross_thread_B_1[0],),
+                    (threadIdx_x,),
+                )
                 if threadIdx_x == 0:
                     B_1 = T.decl_tensor((128,), data=B.data)
                     B_1[i] = cross_thread_B_1[0]
@@ -368,15 +336,15 @@ def test_multi_warp_reduce2():
             threadIdx_x = T.launch_thread("threadIdx.x", 1024)
             cross_thread_B = T.alloc_tensor((1,), scope="local")
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_1 = T.decl_tensor((1024,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1), A_1[threadIdx_x], T.bool(True), 
cross_thread_B_1[0], threadIdx_x
-                )
+            A_1 = T.decl_tensor((1024,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_1[threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((1,), data=B.data)
                 B_1[0] = cross_thread_B_1[0]
@@ -400,19 +368,15 @@ def test_multi_group_multi_warp_reduction():
             cross_thread_B = T.alloc_tensor((1,), scope="local")
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_1 = T.decl_tensor((512,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_1[threadIdx_y * 128 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B_1[0],
-                    threadIdx_x,
-                )
+            A_1 = T.decl_tensor((512,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_1[threadIdx_y * 128 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((4,), data=B.data)
                 B_1[threadIdx_y] = cross_thread_B_1[0]
@@ -442,14 +406,14 @@ def test_multi_group_multi_warp_predicated_reduction():
                 A_1 = T.decl_tensor((140,), data=A.data)
                 in_thread_B_1[0] = in_thread_B_1[0] + A_1[threadIdx_y * 70 + 
threadIdx_x]
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                T.tvm_thread_allreduce(
-                    T.uint32(1), in_thread_B_1[0], T.bool(True), 
cross_thread_B_1[0], threadIdx_x
-                )
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (in_thread_B_1[0],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((2,), data=B.data)
                 B_1[threadIdx_y] = cross_thread_B_1[0]
@@ -486,19 +450,15 @@ def test_metal_no_mask():
             threadIdx_y = T.launch_thread("threadIdx.y", 2)
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_1 = T.decl_tensor((256,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_1[threadIdx_y * 128 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B_1[0],
-                    threadIdx_x,
-                )
+            A_1 = T.decl_tensor((256,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_1[threadIdx_y * 128 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((2,), data=B.data)
                 B_1[threadIdx_y] = cross_thread_B_1[0]
@@ -538,18 +498,14 @@ def test_webgpu_warp_reduce():
                 reduce = T.decl_tensor(1, data=reduce_data.data, scope="local")
                 reduce_alias = T.decl_tensor(1, data=reduce.data, 
scope="local")
 
-                with T.attr(
-                    T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                    "reduce_scope",
-                    T.int32(0),
-                ):
-                    T.tvm_thread_allreduce(
-                        T.uint32(1),
-                        A_flat[0],
-                        T.bool(True),
-                        reduce_alias[0],
-                        threadIdx_x,
-                    )
+                T.tvm_thread_allreduce(
+                    T.Lambda([T.float32, T.float32], lambda x, y: (x + y,)),
+                    (T.float32(0),),
+                    (A_flat[0],),
+                    T.bool(True),
+                    (reduce_alias[0],),
+                    (threadIdx_x,),
+                )
                 if threadIdx_x == 0:
                     B[i] = reduce_alias[0]
 
@@ -588,19 +544,15 @@ def test_webgpu_multi_warp_reduce():
             threadIdx_y = T.launch_thread("threadIdx.y", 2)
             threadIdx_x = T.launch_thread("threadIdx.x", 128)
             cross_thread_B_1 = T.decl_tensor((1,), data=cross_thread_B.data, 
scope="local")
-            with T.attr(
-                T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-                "reduce_scope",
-                T.int32(0),
-            ):
-                A_1 = T.decl_tensor((256,), data=A.data)
-                T.tvm_thread_allreduce(
-                    T.uint32(1),
-                    A_1[threadIdx_y * 128 + threadIdx_x],
-                    T.bool(True),
-                    cross_thread_B_1[0],
-                    threadIdx_x,
-                )
+            A_1 = T.decl_tensor((256,), data=A.data)
+            T.tvm_thread_allreduce(
+                T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+                (T.float32(0),),
+                (A_1[threadIdx_y * 128 + threadIdx_x],),
+                T.bool(True),
+                (cross_thread_B_1[0],),
+                (threadIdx_x,),
+            )
             if threadIdx_x == 0:
                 B_1 = T.decl_tensor((2,), data=B.data)
                 B_1[threadIdx_y] = cross_thread_B_1[0]
diff --git a/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py 
b/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
index e59aa85c08..447b65cd31 100644
--- a/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
+++ b/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
@@ -190,18 +190,14 @@ def test_sync_bind():
         A_temp_4 = T.bind(in_thread_A_temp_1[0] + A_shared_1[threadIdx_x + 
384])
         in_thread_A_temp_1[0] = A_temp_4
         cross_thread_A_temp_1 = T.decl_tensor((1,), 
data=cross_thread_A_temp.data, scope="local")
-        with T.attr(
-            T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-            "reduce_scope",
-            T.int32(0),
-        ):
-            T.tvm_thread_allreduce(
-                T.uint32(1),
-                in_thread_A_temp_1[0],
-                T.bool(True),
-                cross_thread_A_temp_1[0],
-                threadIdx_x,
-            )
+        T.tvm_thread_allreduce(
+            T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+            (T.float32(0),),
+            (in_thread_A_temp_1[0],),
+            T.bool(True),
+            (cross_thread_A_temp_1[0],),
+            (threadIdx_x,),
+        )
 
     @Ts.prim_func(private=True)
     def expected(A: T.Tensor((8192,), "float32")):
@@ -227,17 +223,13 @@ def test_sync_bind():
         cross_thread_A_temp_1_1 = T.decl_tensor(
             (1,), data=cross_thread_A_temp_1.data, scope="local"
         )
-        T.attr(
-            T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]),
-            "reduce_scope",
-            T.int32(0),
-        )
         T.tvm_thread_allreduce(
-            T.uint32(1),
-            in_thread_A_temp_1_1[0],
+            T.Lambda([T.float32, T.float32], lambda x0, y0: (x0 + y0,)),
+            (T.float32(0),),
+            (in_thread_A_temp_1_1[0],),
             T.bool(True),
-            cross_thread_A_temp_1_1[0],
-            threadIdx_x,
+            (cross_thread_A_temp_1_1[0],),
+            (threadIdx_x,),
         )
 
     mod = tvm.IRModule({"main": func})
diff --git a/tests/python/tirx-base/test_tir_op_types.py 
b/tests/python/tirx-base/test_tir_op_types.py
index fad780f8cc..83a3944b81 100644
--- a/tests/python/tirx-base/test_tir_op_types.py
+++ b/tests/python/tirx-base/test_tir_op_types.py
@@ -93,11 +93,12 @@ def test_tir_op_call_likely():
 
 
 def test_tir_op_tvm_thread_allreduce():
-    x = tirx.Var("x", "int32")
-    buffer = tirx.decl_tensor((128), "float32")
-    y = tirx.Var("y", "handle")
-    z = tirx.Var("z", "int32")
-    expr = tirx.tvm_thread_allreduce(x, buffer[0], True, y, z)
+    tensor = tirx.decl_tensor((128,), "float32")
+    axis = tirx.Var("thread", "int32")
+    combine = tvm.ir.LambdaExpr(["float32", "float32"], lambda lhs, rhs: (lhs 
+ rhs,))
+    expr = tirx.tvm_thread_allreduce(
+        combine, (tirx.const(0, "float32"),), (tensor[0],), True, 
(tensor[1],), (axis,)
+    )
     assert expr.op.name == "tirx.tvm_thread_allreduce"
 
 
diff --git a/tests/python/tirx-transform/test_tir_transform_bf16_legalize.py 
b/tests/python/tirx-transform/test_tir_transform_bf16_legalize.py
index d11d11f2ac..4329ed2a21 100644
--- a/tests/python/tirx-transform/test_tir_transform_bf16_legalize.py
+++ b/tests/python/tirx-transform/test_tir_transform_bf16_legalize.py
@@ -359,18 +359,14 @@ def test_bf16_reduce_will_legalize():
 
                     reduce = T.decl_tensor(1, dtype="bfloat16", scope="local")
 
-                    with T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.bfloat16(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    ):
-                        T.tvm_thread_allreduce(
-                            T.uint32(1),
-                            A_flat[0],
-                            T.bool(True),
-                            reduce[0],
-                            threadIdx_x,
-                        )
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.bfloat16, T.bfloat16], lambda x, y: (x + 
y,)),
+                        (T.bfloat16(0),),
+                        (A_flat[0],),
+                        T.bool(True),
+                        (reduce[0],),
+                        (threadIdx_x,),
+                    )
 
         return Before
 
@@ -388,13 +384,10 @@ def test_bf16_reduce_will_legalize():
 
                     reduce = T.decl_tensor(1, dtype="float32", scope="local")
 
-                    with T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    ):
-                        T.tvm_thread_allreduce(
-                            T.uint32(1),
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (
                             T.reinterpret(
                                 "float32",
                                 T.shift_left(
@@ -402,10 +395,11 @@ def test_bf16_reduce_will_legalize():
                                     T.uint32(16),
                                 ),
                             ),
-                            T.bool(True),
-                            reduce[0],
-                            threadIdx_x,
-                        )
+                        ),
+                        T.bool(True),
+                        (reduce[0],),
+                        (threadIdx_x,),
+                    )
 
         return After
 
@@ -423,13 +417,10 @@ def test_bf16_reduce_will_legalize():
 
                     reduce = T.decl_tensor(1, dtype="float32", scope="local")
 
-                    with T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.float32(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    ):
-                        T.tvm_thread_allreduce(
-                            T.uint32(1),
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.float32, T.float32], lambda x, y: (x + 
y,)),
+                        (T.float32(0),),
+                        (
                             T.reinterpret(
                                 "float32",
                                 T.shift_left(
@@ -437,10 +428,11 @@ def test_bf16_reduce_will_legalize():
                                     T.uint32(16),
                                 ),
                             ),
-                            T.bool(True),
-                            reduce[0],
-                            threadIdx_x,
-                        )
+                        ),
+                        T.bool(True),
+                        (reduce[0],),
+                        (threadIdx_x,),
+                    )
 
         return After
 
@@ -467,18 +459,14 @@ def test_bf16_reduce_wont_legalize():
 
                     reduce = T.decl_tensor(1, dtype="bfloat16", scope="local")
 
-                    with T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.bfloat16(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    ):
-                        T.tvm_thread_allreduce(
-                            T.uint32(1),
-                            A_flat[0],
-                            T.bool(True),
-                            reduce[0],
-                            threadIdx_x,
-                        )
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.bfloat16, T.bfloat16], lambda x, y: (x + 
y,)),
+                        (T.bfloat16(0),),
+                        (A_flat[0],),
+                        T.bool(True),
+                        (reduce[0],),
+                        (threadIdx_x,),
+                    )
 
         return Before
 
@@ -496,18 +484,14 @@ def test_bf16_reduce_wont_legalize():
 
                     reduce = T.decl_tensor(1, dtype="bfloat16", scope="local")
 
-                    with T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.bfloat16(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    ):
-                        T.tvm_thread_allreduce(
-                            T.uint32(1),
-                            A_flat[0],
-                            T.bool(True),
-                            reduce[0],
-                            threadIdx_x,
-                        )
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.bfloat16, T.bfloat16], lambda x, y: (x + 
y,)),
+                        (T.bfloat16(0),),
+                        (A_flat[0],),
+                        T.bool(True),
+                        (reduce[0],),
+                        (threadIdx_x,),
+                    )
 
         return After
 
@@ -525,18 +509,14 @@ def test_bf16_reduce_wont_legalize():
 
                     reduce = T.decl_tensor(1, dtype="bfloat16", scope="local")
 
-                    with T.attr(
-                        T.comm_reducer(lambda x, y: x + y, [T.bfloat16(0)]),
-                        "reduce_scope",
-                        T.int32(0),
-                    ):
-                        T.tvm_thread_allreduce(
-                            T.uint32(1),
-                            A_flat[0],
-                            T.bool(True),
-                            reduce[0],
-                            threadIdx_x,
-                        )
+                    T.tvm_thread_allreduce(
+                        T.Lambda([T.bfloat16, T.bfloat16], lambda x, y: (x + 
y,)),
+                        (T.bfloat16(0),),
+                        (A_flat[0],),
+                        T.bool(True),
+                        (reduce[0],),
+                        (threadIdx_x,),
+                    )
 
         return After
 

Reply via email to