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-tirx.git
The following commit(s) were added to refs/heads/main by this push:
new bb07a8c94 [Fix][Sym] Guard IntSet evaluation against floating-point
subexpressions (#216)
bb07a8c94 is described below
commit bb07a8c94571ee339f28af4ca705bc661816dcd9
Author: Shushi Hong <[email protected]>
AuthorDate: Fri Nov 6 17:19:30 2026 -0500
[Fix][Sym] Guard IntSet evaluation against floating-point subexpressions
(#216)
This fixes a crash in IntSet analysis when floating-point expressions
appear in bindings or inside integer casts. The analyzer could apply
integer interval rules to these expressions and try to create an
`IntImm` with a floating-point type.
The relevant handlers now check the expression’s result type and return
`IntervalSet::Everything()` for unsupported types. The checks use
borrowed type pointers to avoid extra reference-count operations.
---
src/sym/int_set.cc | 14 ++++++++++++++
tests/python/sym/test_intset.py | 24 ++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/src/sym/int_set.cc b/src/sym/int_set.cc
index 13285df90..864416232 100644
--- a/src/sym/int_set.cc
+++ b/src/sym/int_set.cc
@@ -428,6 +428,7 @@ class IntervalSetEvaluator : public
tvm::ExprFunctor<IntervalSet(const Expr&)> {
}
IntervalSet Dispatch_(const VarObj* op) final {
+ if (!CheckIntegerOrBoolType(op)) return IntervalSet::Everything();
Var var = ffi::GetRef<Var>(op);
auto it = dom_map_.find(var);
@@ -557,17 +558,20 @@ class IntervalSetEvaluator : public
tvm::ExprFunctor<IntervalSet(const Expr&)> {
}
IntervalSet Dispatch_(const prim::BroadcastObj* op) final {
+ if (!CheckIntegerOrBoolType(op)) return IntervalSet::Everything();
TVM_FFI_ICHECK(eval_vec_);
return Dispatch(op->value);
}
IntervalSet Dispatch_(const prim::SelectObj* op) final {
+ if (!CheckIntegerOrBoolType(op)) return IntervalSet::Everything();
IntervalSet true_set = this->Eval(op->true_value);
IntervalSet false_set = this->Eval(op->false_value);
return Union(analyzer_, false_set, true_set);
}
IntervalSet Dispatch_(const prim::CastObj* op) final {
+ if (!CheckIntegerOrBoolType(op)) return IntervalSet::Everything();
IntervalSet value_set = this->Eval(op->value);
// short cut for the int set.
if (value_set->min_value.same_as(value_set->max_value)) {
@@ -614,6 +618,15 @@ class IntervalSetEvaluator : public
tvm::ExprFunctor<IntervalSet(const Expr&)> {
}
private:
+ // Integer expressions can contain floating-point subexpressions. Only apply
+ // integer interval rules to nodes with an integer or boolean result type.
+ static bool CheckIntegerOrBoolType(const ExprObj* op) {
+ const auto* ty = op->ty.as<PrimTypeObj>();
+ return ty != nullptr &&
+ (ty->dtype.code == DLDataTypeCode::kDLInt || ty->dtype.code ==
DLDataTypeCode::kDLUInt ||
+ ty->dtype.code == DLDataTypeCode::kDLBool);
+ }
+
// whether set is exactly single point that equals value.
bool MatchPoint(const IntervalSet& set, const PrimExpr& value) const {
return set->min_value.same_as(value) && set->max_value.same_as(value);
@@ -622,6 +635,7 @@ class IntervalSetEvaluator : public
tvm::ExprFunctor<IntervalSet(const Expr&)> {
template <typename TOp, typename T>
inline IntervalSet VisitBinaryExpr_(const T* op) {
static_assert(std::is_same<typename TOp::ContainerType, T>::value,
"constraint");
+ if (!CheckIntegerOrBoolType(op)) return IntervalSet::Everything();
IntervalSet a = this->Eval(op->a);
IntervalSet b = this->Eval(op->b);
if (MatchPoint(a, op->a) && MatchPoint(b, op->b)) {
diff --git a/tests/python/sym/test_intset.py b/tests/python/sym/test_intset.py
index 573fe996e..183095101 100644
--- a/tests/python/sym/test_intset.py
+++ b/tests/python/sym/test_intset.py
@@ -154,6 +154,30 @@ def test_select():
)
+def test_float_subexpressions():
+ analyzer = Analyzer()
+ i = tirx.Var("i", "int32")
+ scale = tirx.Var("scale", "float32")
+ coordinate = tirx.Cast("float32", i) * scale
+ domain = {i: tvm_tirx.sym.IntervalSet(0, 4)}
+ for expr in [
+ tvm_tirx.ir.FloatImm("float32", 3.5),
+ scale,
+ tirx.Cast("float32", i),
+ coordinate,
+ tirx.call_pure_extern("returns_float", ty="float32"),
+ ]:
+ assert analyzer.int_set(expr, domain).is_everything()
+ assert analyzer.int_set(tirx.Cast("int64", expr),
domain).is_everything()
+
+ context = tvm_tirx.sym.ConstraintContext()
+ context.append(tvm_tirx.sym.RangeConstraint(i, tvm_tirx.ir.Range(0, 5)))
+ value = tirx.Var("coordinate", "float32")
+ context.append(tvm_tirx.sym.BindingConstraint(value, coordinate))
+ analyzer.sync_constraint_context(context)
+ assert analyzer.int_set(value).is_everything()
+
+
def check_region_bound(expect_region, var_dom, mode, predicate=None):
"""Helper to check region bound estimation.