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 740a83150b [Arith] Remove vscale-specific simplification and analysis
(#20357)
740a83150b is described below
commit 740a83150bdddbd75db550c9ce0e0ce64fdbfa3d
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Sep 16 07:23:10 2026 -0400
[Arith] Remove vscale-specific simplification and analysis (#20357)
Remove arithmetic's vscale-specific patterns, proof shortcuts, and
bounds. Ramp range rewrites require constant lane counts;
lane-independent vector algebra and shared vector/backend support remain
intact.
Removing vscale recognition also removes arithmetic's dependency on TIRX
CheckContains.
---
src/arith/int_set.cc | 20 +---
src/arith/pattern_match.h | 6 --
src/arith/rewrite_simplify.cc | 111 +++++-----------------
tests/cpp/pattern_match_test.cc | 5 +-
tests/python/arith/test_arith_intset.py | 2 +-
tests/python/arith/test_arith_rewrite_simplify.py | 40 +-------
tests/python/arith/test_arith_simplify.py | 44 ---------
7 files changed, 28 insertions(+), 200 deletions(-)
diff --git a/src/arith/int_set.cc b/src/arith/int_set.cc
index 34d96ad5b6..cfda30a96c 100644
--- a/src/arith/int_set.cc
+++ b/src/arith/int_set.cc
@@ -546,18 +546,6 @@ class IntervalSetEvaluator : public
tvm::ExprFunctor<IntervalSet(const Expr&)> {
return Combine<prim::Add>(analyzer_, base, IntervalSet(stride_expr,
IntImm(t, 0)),
add_node);
}
- } else { /* Scalable vector */
- if (vstride > 0) {
- auto add_op = prim::Add(op->base, IntImm(t, 0));
- auto add_node = add_op.as<prim::AddNode>();
- return Combine<prim::Add>(analyzer_, base, IntervalSet(IntImm(t, 0),
pos_inf()),
- add_node);
- } else {
- auto add_op = prim::Add(op->base, IntImm(t, 0));
- auto add_node = add_op.as<prim::AddNode>();
- return Combine<prim::Add>(analyzer_, base, IntervalSet(neg_inf(),
IntImm(t, 0)),
- add_node);
- }
}
}
DLOG(WARNING) << "cannot evaluate set on expression " <<
ffi::GetRef<PrimExpr>(op);
@@ -613,13 +601,7 @@ class IntervalSetEvaluator : public
tvm::ExprFunctor<IntervalSet(const Expr&)> {
return IntervalSet::SinglePoint(ffi::GetRef<PrimExpr>(op));
}
- IntervalSet Dispatch_(const CallNode* op) final {
- if (op->op.same_as(prim::builtin::vscale())) {
- PrimExpr call = ffi::GetRef<Call>(op).as_or_throw<PrimExpr>();
- return IntervalSet(call, call);
- }
- return IntervalSet::Everything();
- }
+ IntervalSet Dispatch_(const CallNode* op) final { return
IntervalSet::Everything(); }
IntervalSet DispatchDefault_(const ffi::Object* op) final {
DLOG(WARNING) << "cannot evaluate set type " << op->GetTypeKey();
diff --git a/src/arith/pattern_match.h b/src/arith/pattern_match.h
index 680afcc947..10a93a489e 100644
--- a/src/arith/pattern_match.h
+++ b/src/arith/pattern_match.h
@@ -842,12 +842,6 @@ inline PCallExpr<PIfThenElseOp, TCond, TA, TB>
if_then_else(const Pattern<TCond>
false_value.derived());
}
-// vscale
-struct PVscaleOp {
- static PrimExpr Eval() { return Call(PrimType::Int(32), GetOp(),
{}).as_or_throw<PrimExpr>(); }
- static const Op& GetOp() { return prim::builtin::vscale(); }
-};
-
template <typename... TPattern>
class PMatchesOneOf {
public:
diff --git a/src/arith/rewrite_simplify.cc b/src/arith/rewrite_simplify.cc
index 2a36f32e4e..97b50d2d50 100644
--- a/src/arith/rewrite_simplify.cc
+++ b/src/arith/rewrite_simplify.cc
@@ -36,7 +36,6 @@
#include <tuple>
#include <utility>
-#include "../tirx/analysis/check_contains.h"
#include "conjunctive_normal_form.h"
#include "const_fold.h"
#include "constraint_extract.h"
@@ -46,34 +45,10 @@ namespace tvm {
namespace arith {
namespace {
-// File-local helper: true if `expr` is a call to prim::builtin::vscale().
-bool IsVScaleCall(const PrimExpr& expr) {
- if (const auto* call = expr.as<CallNode>()) {
- return call->op.same_as(prim::builtin::vscale());
- }
- return false;
-}
-
-// File-local helper: true if `expr` contains a call to
prim::builtin::vscale().
-bool ContainsVscaleCall(const PrimExpr& expr) {
- return tirx::CheckContains::ExprContains(expr, IsVScaleCall);
-}
-
TVM_FFI_INLINE bool IsVectorExpr(const ExprNode* expr) {
PrimType ty = expr->ty.as_or_throw<PrimType>();
return ty.IsScalableVector() || ty.IsFixedLengthVector();
}
-
-// File-local helper: returns the vscale multiplier if `lanes` is of the form
-// `multiplier * vscale()` or `vscale() * multiplier`, nullopt otherwise.
-std::optional<int> ExtractVscaleFactor(const PrimExpr& lanes) {
- PVar<IntImm> multiplier;
- PCallExpr<PVscaleOp> vscale;
- if (PMatchesOneOf(multiplier * vscale, vscale * multiplier).Match(lanes)) {
- return multiplier.Eval()->value;
- }
- return std::nullopt;
-}
} // namespace
using namespace tirx;
@@ -833,11 +808,11 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::DivNode* op,
return ramp(div(b1, c2), div(c1, c2), lanes).Eval();
}
// If all possible indices in ramp are the same.
- if (CanProveGreaterEqual(b1.Eval(), 0) &&
!ExtractVscaleFactor(lanes.Eval())) {
+ if (const auto* lanes_int = lanes.Eval().as<IntImmNode>();
+ lanes_int && CanProveGreaterEqual(b1.Eval(), 0)) {
ModularSet bmod = analyzer_->modular_set(b1.Eval());
int64_t ramp_min = bmod->base / c2val;
- auto lanes_int = lanes.Eval().as<IntImmNode>()->value;
- int64_t ramp_max = (bmod->base + (lanes_int - 1) * c1val) / c2val;
+ int64_t ramp_max = (bmod->base + (lanes_int->value - 1) * c1val) /
c2val;
if (bmod->coeff % c2val == 0 && ramp_min == ramp_max) {
return broadcast(div(b1, c2), lanes).Eval();
}
@@ -992,24 +967,16 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::ModNode* op,
// If all possible indices in ramp are the same.
if (CanProveGreaterEqual(b1.Eval(), 0)) {
ModularSet bmod = analyzer_->modular_set(b1.Eval());
- if (!ExtractVscaleFactor(lanes.Eval())) {
- auto lanes_int = lanes.Eval().as<IntImmNode>()->value;
+ if (const auto* lanes_int = lanes.Eval().as<IntImmNode>()) {
int64_t ramp_min = bmod->base / c2val;
- int64_t ramp_max = (bmod->base + (lanes_int - 1) * c1val) / c2val;
- if (bmod->coeff % c2val == 0) {
- if (ramp_min == ramp_max) {
- return ramp(truncmod(bmod->base, c2), c1, lanes).Eval();
- } else {
- return truncmod(ramp(truncmod(bmod->base, c2), c1, lanes),
broadcast(c2, lanes))
- .Eval();
- }
- }
- } else { /* Special case for scalable vectors */
- ModularSet bmod = analyzer_->modular_set(b1.Eval());
- if (bmod->coeff % c2val == 0) {
- return truncmod(ramp(truncmod(bmod->base, c2), c1, lanes),
broadcast(c2, lanes)).Eval();
+ int64_t ramp_max = (bmod->base + (lanes_int->value - 1) * c1val) /
c2val;
+ if (bmod->coeff % c2val == 0 && ramp_min == ramp_max) {
+ return ramp(truncmod(bmod->base, c2), c1, lanes).Eval();
}
}
+ if (bmod->coeff % c2val == 0) {
+ return truncmod(ramp(truncmod(bmod->base, c2), c1, lanes),
broadcast(c2, lanes)).Eval();
+ }
}
}
}
@@ -1081,18 +1048,18 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::FloorDivNode*
return ramp(floordiv(b1, c2), floordiv(c1, c2), lanes).Eval();
}
// If all possible indices in ramp are the same.
- if (!ExtractVscaleFactor(lanes.Eval())) {
+ if (const auto* lanes_int = lanes.Eval().as<IntImmNode>()) {
ModularSet bmod = analyzer_->modular_set(b1.Eval());
int64_t ramp_min = floordiv(bmod->base, c2val);
- auto lanes_int = lanes.Eval().as<IntImmNode>()->value;
- int64_t ramp_max = floordiv(bmod->base + (lanes_int - 1) * c1val,
c2val);
+ int64_t ramp_max = floordiv(bmod->base + (lanes_int->value - 1) *
c1val, c2val);
if (ramp_min == ramp_max) {
// If b1 can divide c2
if (bmod->coeff % c2val == 0) {
return broadcast(floordiv(b1, c2), lanes).Eval();
}
// If all indices can be guaranteed to settle inside a coeff range
- if (c2val % bmod->coeff == 0 && bmod->base + (lanes_int - 1) * c1val
< bmod->coeff) {
+ if (c2val % bmod->coeff == 0 &&
+ bmod->base + (lanes_int->value - 1) * c1val < bmod->coeff) {
return broadcast(floordiv(b1, c2), lanes).Eval();
}
}
@@ -1199,11 +1166,6 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::FloorDivNode*
CanProveGreaterEqual(z.Eval() * c1.Eval(), 0));
TVM_TRY_REWRITE_IF(floordiv(x - floormod(x, c1), c1), floordiv(x, c1),
c1.Eval()->value != 0);
-
- // Scalable divisor
- TVM_TRY_REWRITE_IF(floordiv(x, y), ZeroWithTypeLike(x),
- ContainsVscaleCall(y.Eval()) &&
CanProveGreaterEqual(x.Eval(), 0) &&
- CanProveGreaterEqual(y.Eval(), 0) &&
CanProve(x.Eval() < y.Eval()));
}
// Unsigned (uint32/uint64): the signed IsIndexType block above is skipped
for
@@ -1288,28 +1250,24 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::FloorModNode*
}
// If all possible indices in ramp are the same.
ModularSet bmod = analyzer_->modular_set(b1.Eval());
- if (!ExtractVscaleFactor(lanes.Eval())) {
+ if (const auto* lanes_int = lanes.Eval().as<IntImmNode>()) {
int64_t ramp_min = floordiv(bmod->base, c2val);
- auto lanes_int = lanes.Eval().as<IntImmNode>()->value;
- int64_t ramp_max = floordiv(bmod->base + (lanes_int - 1) * c1val,
c2val);
+ int64_t ramp_max = floordiv(bmod->base + (lanes_int->value - 1) *
c1val, c2val);
if (ramp_min == ramp_max) {
// If b1 can divide c2
if (bmod->coeff % c2val == 0) {
return ramp(floormod(bmod->base, c2), c1, lanes).Eval();
}
// If all indices can be guaranteed to settle inside a coeff range
- if (c2val % bmod->coeff == 0 && bmod->base + (lanes_int - 1) * c1val
< bmod->coeff) {
+ if (c2val % bmod->coeff == 0 &&
+ bmod->base + (lanes_int->value - 1) * c1val < bmod->coeff) {
return ramp(floormod(b1, c2), c1, lanes).Eval();
}
}
- // If b1 can divide c2
- if (bmod->coeff % c2val == 0) {
- return floormod(ramp(floormod(bmod->base, c2), c1, lanes),
broadcast(c2, lanes)).Eval();
- }
- } else { /* scalable vectors */
- if (bmod->coeff % c2val == 0) {
- return floormod(ramp(floormod(bmod->base, c2), c1, lanes),
broadcast(c2, lanes)).Eval();
- }
+ }
+ // If b1 can divide c2, simplify the base independently of the number of
lanes.
+ if (bmod->coeff % c2val == 0) {
+ return floormod(ramp(floormod(bmod->base, c2), c1, lanes),
broadcast(c2, lanes)).Eval();
}
}
}
@@ -1352,11 +1310,6 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::FloorModNode*
TVM_TRY_REWRITE_IF(floormod(x * z * c1 + y, z * c1), floormod(y, z * c1),
CanProveGreaterEqual(z.Eval() * c1.Eval(), 0));
- // Scalable divisor
- TVM_TRY_REWRITE_IF(floormod(x, y), x,
- ContainsVscaleCall(y.Eval()) &&
CanProveGreaterEqual(x.Eval(), 0) &&
- CanProveGreaterEqual(y.Eval(), 0) &&
CanProve(x.Eval() < y.Eval()));
-
if (floormod(x, c1).Match(ret)) {
int64_t c1val = c1.Eval()->value;
if (c1val > 0) {
@@ -1599,16 +1552,6 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::MinNode* op,
}
}
- // vscale expression comparison
- if (ContainsVscaleCall(op->a) || ContainsVscaleCall(op->b)) {
- if (analyzer_->CanProve(op->a <= op->b)) {
- return op->a;
- }
- if (analyzer_->CanProve(op->b <= op->a)) {
- return op->b;
- }
- }
-
// canonicalization
TVM_TRY_RECURSIVE_REWRITE(min(min(x, c1), y), min(min(x, y), c1));
TVM_TRY_RECURSIVE_REWRITE_IF(min(c1 - x, c2), c1 - max(x, c1 - c2),
c2.Eval()->value != 0);
@@ -1794,16 +1737,6 @@ UnchangedOr<PrimExpr>
RewriteSimplifier::Impl::Mutate_(const prim::MaxNode* op,
}
}
- // vscale expression comparison
- if (ContainsVscaleCall(op->a) || ContainsVscaleCall(op->b)) {
- if (analyzer_->CanProve(op->a >= op->b)) {
- return op->a;
- }
- if (analyzer_->CanProve(op->b >= op->a)) {
- return op->b;
- }
- }
-
// canonicalization
TVM_TRY_RECURSIVE_REWRITE(max(max(x, c1), y), max(max(x, y), c1));
TVM_TRY_RECURSIVE_REWRITE_IF(max(c1 - x, c2), c1 - min(x, c1 - c2),
c2.Eval()->value != 0);
diff --git a/tests/cpp/pattern_match_test.cc b/tests/cpp/pattern_match_test.cc
index 89973c7868..f53b8acec1 100644
--- a/tests/cpp/pattern_match_test.cc
+++ b/tests/cpp/pattern_match_test.cc
@@ -31,7 +31,6 @@ TEST(Pattern, Basic) {
arith::PVar<PrimExpr> px, py, pz;
arith::PVar<DLDataType> pt;
arith::PVar<PrimExpr> planes;
- arith::PCallExpr<PVscaleOp> vscale;
// arithmetics
auto r = 1 + (y + 1);
@@ -116,7 +115,7 @@ TEST(Pattern, Basic) {
TVM_FFI_ICHECK(ramp(px, PConst<PrimExpr>(1), planes).Match(prim::Ramp(x,
1, 10)));
TVM_FFI_ICHECK(planes.Eval().as<IntImmNode>()->value == 10);
TVM_FFI_ICHECK(ramp(px, PConst<PrimExpr>(1), planes).Match(prim::Ramp(x,
1, scalable_lanes)));
- TVM_FFI_ICHECK((vscale * PConst<PrimExpr>(4)).Match(planes.Eval()));
+ TVM_FFI_ICHECK(tirx::ExprDeepEqual()(planes.Eval(), scalable_lanes));
TVM_FFI_ICHECK(!ramp(px, PConst<PrimExpr>(1), planes).Match(prim::Ramp(x,
2, 10)));
}
// broadcast pattern
@@ -125,7 +124,7 @@ TEST(Pattern, Basic) {
TVM_FFI_ICHECK(planes.Eval().as<IntImmNode>()->value == 10);
TVM_FFI_ICHECK(broadcast(px * py, planes).Match(prim::Broadcast(x * 10,
10)));
TVM_FFI_ICHECK(broadcast(px, planes).Match(prim::Broadcast(x,
scalable_lanes)));
- TVM_FFI_ICHECK((vscale * PConst<PrimExpr>(4)).Match(planes.Eval()));
+ TVM_FFI_ICHECK(tirx::ExprDeepEqual()(planes.Eval(), scalable_lanes));
}
}
diff --git a/tests/python/arith/test_arith_intset.py
b/tests/python/arith/test_arith_intset.py
index e83a8c9f9d..223b73f49d 100644
--- a/tests/python/arith/test_arith_intset.py
+++ b/tests/python/arith/test_arith_intset.py
@@ -58,7 +58,7 @@ def test_scalable_vector():
base = 5
s = tvm.arith.IntSet.vector(tvm.tirx.Ramp(base, 2, tvm.tirx.vscale() * 4))
- assert s.min_value.value == base
+ assert s.min_value.same_as(tvm.arith.int_set.neg_inf())
assert s.max_value.same_as(tvm.arith.int_set.pos_inf())
diff --git a/tests/python/arith/test_arith_rewrite_simplify.py
b/tests/python/arith/test_arith_rewrite_simplify.py
index 5d0929a1e4..0194392ea2 100644
--- a/tests/python/arith/test_arith_rewrite_simplify.py
+++ b/tests/python/arith/test_arith_rewrite_simplify.py
@@ -899,6 +899,8 @@ class TestMaxIndex(BaseCompare):
x, y, z = tvm.tirx.Var("x", "int32"), tvm.tirx.Var("y", "int32"),
tvm.tirx.Var("z", "int32")
test_case = tvm.testing.parameter(
+ # Identical operands simplify even when they contain an opaque call.
+ TestCase(tvm.tirx.max(x + tirx.vscale() * 4, x + tirx.vscale() * 4), x
+ tirx.vscale() * 4),
# const int bound
TestCase(tvm.tirx.max(tmod(x, 2), tmod(y, 2) + 10), tmod(y, 2) + 10),
TestCase(tvm.tirx.max(flm(x, 2), flm(y, 2) + 10), flm(y, 2) + 10),
@@ -978,44 +980,6 @@ class TestMaxIndex(BaseCompare):
)
-# These simplifications relied on arith::CanProve being able to prove
-# vscale-bearing inequalities (e.g. vscale() > 0) by substituting known
-# vscale values for the current VLA target. That proof loop has been removed
-# from the arith layer -- arith no longer attempts to reason about scalable
-# vector lengths at the target level. The simplifications are correct in
-# principle but can no longer be proven without the substitution loop.
[email protected](reason="arith no longer proves vscale-bearing inequalities
via substitution")
-class TestScalableIndex(BaseCompare):
- x, y = tvm.tirx.Var("x", "int32"), tvm.tirx.Var("y", "int32")
- test_case = tvm.testing.parameter(
- # MinNode
- TestCase(tvm.tirx.min(x + tirx.vscale() * 4, x), x),
- TestCase(tvm.tirx.min(x - tirx.vscale() * 4, x), x + tirx.vscale() *
-4),
- TestCase(tvm.tirx.min(x + tirx.vscale() * 4, x + tirx.vscale() * 8),
tirx.vscale() * 4 + x),
- TestCase(tvm.tirx.min(x + tirx.vscale() * 4 - flm(4, tirx.vscale() *
4), x), x),
- TestCase(tvm.tirx.min(tirx.vscale() * x, tirx.vscale() * y),
tirx.vscale() * x, x < y),
- # MaxNode
- TestCase(tvm.tirx.max(x + tirx.vscale() * 4, x), x + tirx.vscale() *
4),
- TestCase(tvm.tirx.max(x - tirx.vscale() * 4, x), x),
- TestCase(tvm.tirx.max(x + tirx.vscale() * 4, x + tirx.vscale() * 4), x
+ tirx.vscale() * 4),
- TestCase(
- tvm.tirx.max(x + tirx.vscale() * 4 - flm(4, tirx.vscale() * 4), x),
- x + tirx.vscale() * 4 - flm(4, tirx.vscale() * 4),
- ),
- TestCase(tvm.tirx.max(tirx.vscale() * x, tirx.vscale() * y),
tirx.vscale() * x, x > y),
- # FloorDiv
- TestCase(fld(x * tirx.vscale() * 4 + y, tirx.vscale() * 4), x + fld(y,
tirx.vscale() * 4)),
- TestCase(fld(x, tirx.vscale() * 4), 0, [x >= 0, x < tirx.vscale() *
4]),
- # FloorMod
- TestCase(flm(x * tirx.vscale() * 4 + y, tirx.vscale() * 4), flm(y,
tirx.vscale() * 4)),
- TestCase(flm(x, tirx.vscale() * 4), x, [x >= 0, x < tirx.vscale() *
4]),
- )
-
- def test_simplify(self, test_case):
- with tvm.target.Target({"kind": "llvm", "mtriple":
"aarch64-linux-gnu", "mattr": ["+sve"]}):
- super().test_simplify(test_case)
-
-
class TestComparisons(BaseCompare):
x, y, z = tvm.tirx.Var("x", "int32"), tvm.tirx.Var("y", "int32"),
tvm.tirx.Var("z", "int32")
diff --git a/tests/python/arith/test_arith_simplify.py
b/tests/python/arith/test_arith_simplify.py
index fc61068249..09b6a7e1aa 100644
--- a/tests/python/arith/test_arith_simplify.py
+++ b/tests/python/arith/test_arith_simplify.py
@@ -21,7 +21,6 @@ import tvm
import tvm.ir
import tvm.testing
from tvm import tirx
-from tvm.script import tirx as T
def test_simplify_reshape_flattened_index():
@@ -90,49 +89,6 @@ def test_simplify_symbolic_comparison():
assert ana.can_prove((n + 31) // 32 * 32 >= i0 * 32 + i1,
PS.SYMBOLIC_BOUND)
-# These tests exercised arith::CanProve's substitution-based proof loop for
-# vscale-bearing expressions (iterating over known vscale values for a VLA
target).
-# That loop has been removed -- arith no longer attempts target-dependent
proofs
-# about scalable-vector lengths. The LOG(WARNING) for non-VLA targets is also
gone.
[email protected](reason="arith no longer proves vscale-bearing inequalities
via substitution")
[email protected](
- "expression",
- [
- T.vscale() * 32 < T.vscale() * 64,
- T.vscale() * 2 * (T.vscale() * 2) >= T.vscale() * 4,
- (T.vscale() * 4 + 114) // (T.vscale() * 4) * (T.vscale() * 4) >= 115,
- 64 % T.vscale() <= T.vscale(),
- ],
-)
-def test_simplify_vscale_comparison_with_sve_target(expression):
- ana = tvm.arith.Analyzer()
-
- with tvm.target.Target({"kind": "llvm", "mtriple": "aarch64-linux-gnu",
"mattr": ["+sve"]}):
- assert ana.can_prove(expression)
-
-
[email protected](
- reason="arith no longer emits a LOG(WARNING) for vscale proofs on non-VLA
targets"
-)
-def test_simplify_vscale_comparison_without_sve_target(capfd):
- ana = tvm.arith.Analyzer()
- vs = tvm.tirx.vscale()
-
- with pytest.raises(AssertionError):
- with tvm.target.Target({"kind": "llvm", "mtriple":
"aarch64-linux-gnu"}):
- assert ana.can_prove(vs * 32 < vs * 64)
-
- warning_prefix = (
- "Warning: The expression contains scalable values. An attempt to prove
by substituting "
- "with known values of vscale was not performed. This proof currently
only supports "
- "VLA targets, but the target was "
- )
- capture = capfd.readouterr().err
- assert warning_prefix in capture
- assert '"kind":"llvm"' in capture
- assert '"mtriple":"aarch64-linux-gnu"' in capture
-
-
def test_regression_simplify_inf_recursion():
ana = tvm.arith.Analyzer()
cond = tirx.Var("cond", "int32")