From bbb09fb5f56f2ea3b9ae41ec8d5da7e90ceda2a3 Mon Sep 17 00:00:00 2001 From: Martijn Courteaux Date: Thu, 13 Aug 2026 18:57:19 +0200 Subject: [PATCH 1/3] Fix cross-propagation of known alignment and bounds information when learn_true(varA == varB) is processed. Co-authored-by: Alex Reinking --- src/Simplify.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Simplify.cpp b/src/Simplify.cpp index 87fc127d0d8d..75b908ce8202 100644 --- a/src/Simplify.cpp +++ b/src/Simplify.cpp @@ -183,12 +183,39 @@ void Simplify::ScopedFact::learn_true(const Expr &fact) { const Mod *m = eq->a.as(); auto modulus = m ? as_const_int(m->b) : std::nullopt; auto remainder = m ? as_const_int(eq->b) : std::nullopt; + // TODO(mcourteaux): A lot of the logic below is hard-coded to let information + // propagate either from the LHS to the RHS or the other way. There is also + // special case for when varA == varB is given, to let the info cross-propagate. + // All of this feels a little conflated and might get clearer if we figure out a + // neat way to write this down where info can just transparently flow + // in whichever direction is relevant without having to list all these cases. if (v) { - if (is_const(eq->b) || eq->b.as()) { + if (is_const(eq->b)) { + info.replacement = eq->b; + simplify->var_info.push(v->name, info); + pop_list.push_back(v); + } else if (const auto *vb = eq->b.as()) { // TODO: consider other cases where we might want to entirely substitute info.replacement = eq->b; simplify->var_info.push(v->name, info); pop_list.push_back(v); + + // Cross-merge the expression info of both variables. + Simplify::ExprInfo expr_info; + if (const auto *info = simplify->bounds_and_alignment_info.find(v->name)) { + // We already know something about the variable on the LHS + expr_info = *info; + } + if (const auto *info = simplify->bounds_and_alignment_info.find(vb->name)) { + // We already know something about the variable on the RHS + expr_info.intersect(*info); + } + + simplify->bounds_and_alignment_info.push(v->name, expr_info); + simplify->bounds_and_alignment_info.push(vb->name, expr_info); + + bounds_pop_list.push_back(v); + bounds_pop_list.push_back(vb); } else if (v->type.is_int()) { // Visit the rhs again to get bounds and alignment info to propagate to the LHS // TODO: Visiting it again is inefficient @@ -202,7 +229,7 @@ void Simplify::ScopedFact::learn_true(const Expr &fact) { bounds_pop_list.push_back(v); } } else if (const Variable *vb = eq->b.as()) { - // y % 2 == x + // ... == x // We know that LHS is not a const due to // canonicalization, and that the LHS is not a variable or // the case above would have triggered. Learn from the From 90b97b142a9605a70749371b8619e5892a438274 Mon Sep 17 00:00:00 2001 From: Martijn Courteaux Date: Thu, 13 Aug 2026 18:58:07 +0200 Subject: [PATCH 2/3] Return const Expr from Dimension::min/extent/stride/max to help avoid accidental assignment. --- src/Dimension.cpp | 8 ++++---- src/Dimension.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Dimension.cpp b/src/Dimension.cpp index bb152d60c85f..c85d1d5893ad 100644 --- a/src/Dimension.cpp +++ b/src/Dimension.cpp @@ -18,19 +18,19 @@ Dimension::Dimension(const Parameter &p, int d, Func f) << " of a " << param.dimensions() << "-dimensional Parameter\n"; } -Expr Dimension::min() const { +const Expr Dimension::min() const { std::ostringstream s; s << param.name() << ".min." << d; return Variable::make(Int(32), s.str(), param); } -Expr Dimension::extent() const { +const Expr Dimension::extent() const { std::ostringstream s; s << param.name() << ".extent." << d; return Variable::make(Int(32), s.str(), param); } -Expr Dimension::max() const { +const Expr Dimension::max() const { return min() + extent() - 1; } @@ -44,7 +44,7 @@ Expr Dimension::extent_estimate() const { return param.extent_constraint_estimate(d); } -Expr Dimension::stride() const { +const Expr Dimension::stride() const { std::ostringstream s; s << param.name() << ".stride." << d; return Variable::make(Int(32), s.str(), param); diff --git a/src/Dimension.h b/src/Dimension.h index c386181248e1..a4f681263c46 100644 --- a/src/Dimension.h +++ b/src/Dimension.h @@ -17,19 +17,19 @@ class Dimension { public: /** Get an expression representing the minimum coordinates of this image * parameter in the given dimension. */ - Expr min() const; + const Expr min() const; /** Get an expression representing the extent of this image * parameter in the given dimension */ - Expr extent() const; + const Expr extent() const; /** Get an expression representing the maximum coordinates of * this image parameter in the given dimension. */ - Expr max() const; + const Expr max() const; /** Get an expression representing the stride of this image in the * given dimension */ - Expr stride() const; + const Expr stride() const; /** Set the min in a given dimension to equal the given * expression. Setting the mins to zero may simplify some From 20ff39e95391c3890a41b5e8adaa04613425592e Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Thu, 13 Aug 2026 16:26:33 -0400 Subject: [PATCH 3/3] Revert "Return const Expr from Dimension::min/extent/stride/max to help avoid accidental assignment." This reverts commit 90b97b142a9605a70749371b8619e5892a438274. --- src/Dimension.cpp | 8 ++++---- src/Dimension.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Dimension.cpp b/src/Dimension.cpp index c85d1d5893ad..bb152d60c85f 100644 --- a/src/Dimension.cpp +++ b/src/Dimension.cpp @@ -18,19 +18,19 @@ Dimension::Dimension(const Parameter &p, int d, Func f) << " of a " << param.dimensions() << "-dimensional Parameter\n"; } -const Expr Dimension::min() const { +Expr Dimension::min() const { std::ostringstream s; s << param.name() << ".min." << d; return Variable::make(Int(32), s.str(), param); } -const Expr Dimension::extent() const { +Expr Dimension::extent() const { std::ostringstream s; s << param.name() << ".extent." << d; return Variable::make(Int(32), s.str(), param); } -const Expr Dimension::max() const { +Expr Dimension::max() const { return min() + extent() - 1; } @@ -44,7 +44,7 @@ Expr Dimension::extent_estimate() const { return param.extent_constraint_estimate(d); } -const Expr Dimension::stride() const { +Expr Dimension::stride() const { std::ostringstream s; s << param.name() << ".stride." << d; return Variable::make(Int(32), s.str(), param); diff --git a/src/Dimension.h b/src/Dimension.h index a4f681263c46..c386181248e1 100644 --- a/src/Dimension.h +++ b/src/Dimension.h @@ -17,19 +17,19 @@ class Dimension { public: /** Get an expression representing the minimum coordinates of this image * parameter in the given dimension. */ - const Expr min() const; + Expr min() const; /** Get an expression representing the extent of this image * parameter in the given dimension */ - const Expr extent() const; + Expr extent() const; /** Get an expression representing the maximum coordinates of * this image parameter in the given dimension. */ - const Expr max() const; + Expr max() const; /** Get an expression representing the stride of this image in the * given dimension */ - const Expr stride() const; + Expr stride() const; /** Set the min in a given dimension to equal the given * expression. Setting the mins to zero may simplify some