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 9a4ae22c0b [REFACTOR][Sym] Simplify symbolic analysis dependencies
(#20694)
9a4ae22c0b is described below
commit 9a4ae22c0b9b6c7169149c17f6dcf6481f0daf1c
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Oct 12 22:42:12 2026 -0400
[REFACTOR][Sym] Simplify symbolic analysis dependencies (#20694)
Expose an analyzer-aware `sym::Intersect` overload and use public
`IntSet` operations in loop partitioning. Replace native GCD/LCM helpers
with the standard library while retaining BigInt handling and localizing
extended Euclidean coefficients to modular-set analysis.
Use ordinary node checks for the S-TIR pattern consumers and direct
analyzer simplification in the Relax DFPattern matcher. Retire S-TIR
`UseAssumeToReduceBranches` and its public entry points, removing the
remaining private constraint-extraction dependency.
Remove the unused PresburgerSet C++/Python API and its MLIR build
plumbing while preserving optional Analyzer/Z3 support.
---
CMakeLists.txt | 1 -
cmake/config.cmake | 4 -
cmake/modules/LLVM.cmake | 3 -
cmake/utils/FindLLVM.cmake | 12 -
include/tvm/s_tir/transform.h | 6 -
include/tvm/sym/int_set.h | 9 +
python/tvm/ir/json_compact.py | 1 -
python/tvm/s_tir/transform/transform.py | 11 -
python/tvm/sym/__init__.py | 1 -
python/tvm/sym/int_set.py | 8 -
src/relax/ir/dataflow_matcher.cc | 33 +-
src/relax/ir/dataflow_matcher.h | 21 +-
src/s_tir/analysis/conditional_bounds.cc | 13 +-
src/s_tir/schedule/primitive/compute_at.cc | 42 +-
src/s_tir/schedule/primitive/decompose_padding.cc | 7 +-
src/s_tir/schedule/primitive/rolling_buffer.cc | 31 +-
src/s_tir/schedule/utils.h | 30 +-
src/s_tir/transform/compact_tensor_region.cc | 12 +-
src/s_tir/transform/loop_partition.cc | 40 +-
.../transform/using_assume_to_reduce_branches.cc | 422 --------------
src/sym/const_int_bound.cc | 5 +-
src/sym/int_operator.h | 74 +--
src/sym/int_set.cc | 4 +
src/sym/modular_set.cc | 51 +-
src/sym/presburger_set.cc | 293 ----------
src/sym/presburger_set.h | 201 -------
src/sym/rewrite_simplify.cc | 7 +-
...eliminate_pad_branch_using_buffer_assumption.py | 646 ---------------------
28 files changed, 173 insertions(+), 1815 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1b2c4d764e..7a8663ffb2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,7 +69,6 @@ tvm_option(USE_HEXAGON_EXTERNAL_LIBS "Path to git repo
containing external Hexag
tvm_option(USE_RPC "Build with RPC" ON)
tvm_option(USE_THREADS "Build with thread support" ON)
tvm_option(USE_LLVM "Build with LLVM, can be set to specific llvm-config path"
OFF)
-tvm_option(USE_MLIR "Build with MLIR support" OFF)
tvm_option(USE_OPENMP "Build with OpenMP thread pool implementation" OFF)
tvm_option(TVM_DEBUG_WITH_ABI_CHANGE "Enable debug code that may cause ABI
changes" OFF)
tvm_option(TVM_LOG_BEFORE_THROW "Whether log before throw, for debugging
purposes" OFF)
diff --git a/cmake/config.cmake b/cmake/config.cmake
index 068b17fbf0..83e81aa137 100644
--- a/cmake/config.cmake
+++ b/cmake/config.cmake
@@ -131,10 +131,6 @@ set(USE_IOS_RPC OFF)
# - /path/to/llvm-config: enable specific LLVM when multiple llvm-dev is
available.
set(USE_LLVM OFF)
-# Whether use MLIR to help analyze, requires USE_LLVM is enabled
-# Possible values: ON/OFF
-set(USE_MLIR OFF)
-
#---------------------------------------------
# Contrib libraries
#---------------------------------------------
diff --git a/cmake/modules/LLVM.cmake b/cmake/modules/LLVM.cmake
index a7fa4233ac..828526d8dc 100644
--- a/cmake/modules/LLVM.cmake
+++ b/cmake/modules/LLVM.cmake
@@ -51,9 +51,6 @@ if(NOT ${USE_LLVM} MATCHES ${IS_FALSE_PATTERN})
message(STATUS "Set TVM_LLVM_VERSION=" ${TVM_LLVM_VERSION})
# Set flags that are only needed for LLVM target
add_definitions(-DTVM_LLVM_VERSION=${TVM_LLVM_VERSION})
- if (${TVM_MLIR_VERSION})
- add_definitions(-DTVM_MLIR_VERSION=${TVM_MLIR_VERSION})
- endif()
add_definitions(-DTVM_LLVM_HAS_AARCH64_TARGET=${TVM_LLVM_HAS_AARCH64_TARGET})
tvm_file_glob(GLOB COMPILER_LLVM_SRCS
src/target/llvm/*.cc
diff --git a/cmake/utils/FindLLVM.cmake b/cmake/utils/FindLLVM.cmake
index 1f54ded1d5..fc6796a361 100644
--- a/cmake/utils/FindLLVM.cmake
+++ b/cmake/utils/FindLLVM.cmake
@@ -175,18 +175,6 @@ macro(find_llvm use_llvm)
if("AArch64" IN_LIST BUILT_TARGET_LIST)
set(TVM_LLVM_HAS_AARCH64_TARGET 1)
endif()
- if (${USE_MLIR})
- if (EXISTS "${__llvm_libdir}/libMLIRPresburger.a")
- if (EXISTS "${__llvm_libdir}/libMLIRSupport.a")
- message(STATUS "Found MLIR")
- list(APPEND LLVM_LIBS "${__llvm_libdir}/libMLIRPresburger.a")
- list(APPEND LLVM_LIBS "${__llvm_libdir}/libMLIRSupport.a")
- set(TVM_MLIR_VERSION ${TVM_LLVM_VERSION})
- message(STATUS "Build with MLIR")
- message(STATUS "Set TVM_MLIR_VERSION=" ${TVM_MLIR_VERSION})
- endif()
- endif()
- endif()
separate_arguments(__llvm_system_libs)
foreach(__flag IN ITEMS ${__llvm_system_libs})
if("${__flag}" STREQUAL "-lm")
diff --git a/include/tvm/s_tir/transform.h b/include/tvm/s_tir/transform.h
index a1ce06e6e5..4bc593d170 100644
--- a/include/tvm/s_tir/transform.h
+++ b/include/tvm/s_tir/transform.h
@@ -298,12 +298,6 @@ TVM_DLL Pass RemoveWeightLayoutRewriteBlock(bool
skip_tensor_rewrite = false);
*/
TVM_DLL Pass RemoveStoreUndef();
-/*!
- * \brief Eliminate branches by leveraging tensor assumptions (T.assume).
- * \return The pass.
- */
-TVM_DLL Pass UseAssumeToReduceBranches();
-
} // namespace transform
} // namespace s_tir
} // namespace tvm
diff --git a/include/tvm/sym/int_set.h b/include/tvm/sym/int_set.h
index d5a9c3f6be..10132b4f9c 100644
--- a/include/tvm/sym/int_set.h
+++ b/include/tvm/sym/int_set.h
@@ -262,6 +262,15 @@ ffi::Array<IntSet> UnionRegionLowerBound(const
ffi::Array<ffi::Array<IntSet>>& n
*/
IntSet Intersect(const ffi::Array<IntSet>& sets);
+/*!
+ * \brief Intersect two integer sets using the analyzer's bindings and
constraints.
+ * \param lhs The first set.
+ * \param rhs The second set.
+ * \param analyzer The analyzer used to prove whether the intersection is
empty.
+ * \return Nothing if emptiness is proved, or the intersected bounds otherwise.
+ */
+TVM_TIRX_DLL IntSet Intersect(const IntSet& lhs, const IntSet& rhs, const
Analyzer& analyzer);
+
/*!
* \brief Converts the Ranges to IntSets
* \param var_dom The ranges of variables
diff --git a/python/tvm/ir/json_compact.py b/python/tvm/ir/json_compact.py
index 8edc672588..66a76c5cfe 100644
--- a/python/tvm/ir/json_compact.py
+++ b/python/tvm/ir/json_compact.py
@@ -29,7 +29,6 @@ _PRIM_TYPE_KEY_RENAMES = {
"arith.IterSplitExpr": "sym.IterSplitExpr",
"arith.IterSumExpr": "sym.IterSumExpr",
"arith.ModularSet": "sym.ModularSet",
- "arith.PresburgerSet": "sym.PresburgerSet",
"arith.RewriteSimplifierStats": "sym.RewriteSimplifierStats",
"arith.SplitExpr": "sym.SplitExpr",
"arith.SumExpr": "sym.SumExpr",
diff --git a/python/tvm/s_tir/transform/transform.py
b/python/tvm/s_tir/transform/transform.py
index 9130ebcee2..991ccecde9 100644
--- a/python/tvm/s_tir/transform/transform.py
+++ b/python/tvm/s_tir/transform/transform.py
@@ -399,14 +399,3 @@ def RemoveStoreUndef():
The result pass
"""
return _ffi_api.RemoveStoreUndef() # type: ignore
-
-
-def UseAssumeToReduceBranches():
- """Eliminate layout specific pad branch by overcomputing values for padded
region.
-
- Returns
- -------
- fpass : tvm.transform.Pass
- The result pass
- """
- return _ffi_api.UseAssumeToReduceBranches() # type: ignore
diff --git a/python/tvm/sym/__init__.py b/python/tvm/sym/__init__.py
index 0f76bc14db..3a3e091eff 100644
--- a/python/tvm/sym/__init__.py
+++ b/python/tvm/sym/__init__.py
@@ -20,7 +20,6 @@
from .int_set import (
IntSet,
IntervalSet,
- PresburgerSet,
estimate_region_lower_bound,
estimate_region_strict_bound,
estimate_region_upper_bound,
diff --git a/python/tvm/sym/int_set.py b/python/tvm/sym/int_set.py
index 2b2307cd10..5e93f6362e 100644
--- a/python/tvm/sym/int_set.py
+++ b/python/tvm/sym/int_set.py
@@ -85,14 +85,6 @@ class IntervalSet(IntSet):
self.__init_handle_by_constructor__(_ffi_api.IntervalSet, min_value,
max_value)
-@tvm_ffi.register_object("sym.PresburgerSet")
-class PresburgerSet(IntSet):
- """Represent of Presburger Set"""
-
- def __init__(self):
- self.__init_handle_by_constructor__(_ffi_api.PresburgerSet)
-
-
def estimate_region_lower_bound(region, var_dom, predicate, analyzer=None):
"""Analyze the region with affine map, given the domain of variables and
their predicate
Some subregion may be discarded during the lower-bound analysis.
diff --git a/src/relax/ir/dataflow_matcher.cc b/src/relax/ir/dataflow_matcher.cc
index 8ec76a33d2..93d5d09450 100644
--- a/src/relax/ir/dataflow_matcher.cc
+++ b/src/relax/ir/dataflow_matcher.cc
@@ -48,7 +48,6 @@
#include <utility>
#include <vector>
-#include "../../sym/constraint_extract.h"
#include "../transform/utils.h"
namespace tvm {
@@ -442,7 +441,7 @@ bool DFPatternMatcher::VisitDFPattern_(const
TypePatternNode* op, const Expr& ex
return static_cast<bool>(as_int->value);
}
- symbolic_expr_condition_ = SimplifyCondition(symbolic_expr_condition_ &&
new_constraint);
+ symbolic_expr_condition_ = analyzer_->Simplify(symbolic_expr_condition_ &&
new_constraint);
if (auto* as_int = symbolic_expr_condition_.as<IntImmNode>()) {
return static_cast<bool>(as_int->value);
@@ -451,36 +450,6 @@ bool DFPatternMatcher::VisitDFPattern_(const
TypePatternNode* op, const Expr& ex
}
}
-PrimExpr DFPatternMatcher::SimplifyCondition(PrimExpr condition) {
- if (condition->IsInstance<IntImmNode>()) {
- return condition;
- }
-
- std::vector<PrimExpr> constraints = sym::ExtractConstraints(condition,
false);
- if (constraints.size() == 1) {
- return condition;
- }
-
- auto sort_key = [](PrimExpr expr) -> ffi::String {
- if (const auto* equal = expr.as<prim::EQNode>()) {
- if (auto var = equal->a.as<PrimVar>()) {
- return var.value()->name;
- }
- }
- return "";
- };
- std::stable_sort(
- constraints.begin(), constraints.end(),
- [&sort_key](const PrimExpr& a, const PrimExpr& b) { return sort_key(a) <
sort_key(b); });
-
- PrimExpr sorted_condition = IntImm::Bool(true);
- for (const PrimExpr& constraint : constraints) {
- sorted_condition = sorted_condition && constraint;
- }
-
- return analyzer_->Simplify(sorted_condition);
-}
-
static bool ShapeEqual(AnalyzerObj* analyzer, const ffi::Array<PrimExpr>& lhs,
const ffi::Array<PrimExpr>& rhs) {
if (lhs.size() != rhs.size()) return false;
diff --git a/src/relax/ir/dataflow_matcher.h b/src/relax/ir/dataflow_matcher.h
index 1869b2a4d3..677b5526b8 100644
--- a/src/relax/ir/dataflow_matcher.h
+++ b/src/relax/ir/dataflow_matcher.h
@@ -41,8 +41,12 @@ class DFPatternMatcher : public DFPatternFunctor<bool(const
DFPattern&, const Ex
public:
using var2val_t = ffi::Map<Var, Expr>;
- explicit DFPatternMatcher() {}
- explicit DFPatternMatcher(var2val_t var2val) : var2val_(std::move(var2val))
{}
+ explicit DFPatternMatcher() : DFPatternMatcher(var2val_t{}) {}
+ explicit DFPatternMatcher(var2val_t var2val) : var2val_(std::move(var2val)) {
+ // Propagate shape constraints across conjunctions regardless of operand
order.
+ analyzer_->rewrite_simplify.SetEnabledExtensions(
+ sym::RewriteSimplifier::kApplyConstraintsToBooleanBranches);
+ }
bool Match(const DFPattern& pattern, const Expr& expr);
ffi::Map<DFPattern, Expr> GetMemo() { return memo_; }
@@ -78,19 +82,6 @@ class DFPatternMatcher : public DFPatternFunctor<bool(const
DFPattern&, const Ex
const tvm::ffi::Array<Expr> fields,
std::vector<int8_t>& match_cache,
std::vector<bool>& matched);
- /* \brief Simplify a boolean condition using the analyzer
- *
- * Matching type can often produce conditions that do not
- * simplify cleanly. For example, while the rewrite simplifier can
- * recognize that `m==0 && m==1` can be simplifies to `false`, it
- * cannot recognize that `m==0 && n==0 && m==1` can be simplified to
- * false.
- *
- * This function applies additional simplification steps to handle
- * these cases, before delgating to `analyzer_.Simplify`.
- */
- PrimExpr SimplifyCondition(PrimExpr condition);
-
std::unordered_map<DFPattern, Expr, ffi::ObjectPtrHash, ffi::ObjectPtrEqual>
memo_;
var2val_t var2val_;
std::vector<DFPattern> matched_nodes_;
diff --git a/src/s_tir/analysis/conditional_bounds.cc
b/src/s_tir/analysis/conditional_bounds.cc
index c5d2340610..a5de8ec221 100644
--- a/src/s_tir/analysis/conditional_bounds.cc
+++ b/src/s_tir/analysis/conditional_bounds.cc
@@ -33,10 +33,9 @@
#include <algorithm>
#include <functional>
+#include <numeric>
#include <utility>
-#include "../../sym/int_operator.h"
-
namespace tvm {
namespace s_tir {
using namespace tvm::prim;
@@ -48,9 +47,6 @@ using sym::EvalSet;
using sym::IntSet;
namespace {
-using sym::ExtendedEuclidean;
-using sym::LeastCommonMultiple;
-
// The solver's intermediate representations remain local to this analysis.
struct IntGroupBounds {
PrimExpr coef;
@@ -418,10 +414,9 @@ PartialSolvedInequalities SolveLinearInequalities(const
IntConstraints& system_t
&coef_neg, analyzer.get());
// Combine each positive inequality with each negative one (by adding them
together)
- int64_t gcd_x, gcd_y;
for (const auto& pos : coef_pos) {
for (const auto& neg : coef_neg) {
- auto first_gcd = ExtendedEuclidean(pos.first, -neg.first, &gcd_x,
&gcd_y);
+ auto first_gcd = std::gcd(pos.first, -neg.first);
PrimType v_ty = v.ty();
PrimExpr c_pos = prim::MakeConst(v_ty, neg.first / first_gcd);
PrimExpr c_neg = IntImm(v_ty, pos.first / first_gcd);
@@ -443,10 +438,10 @@ PartialSolvedInequalities SolveLinearInequalities(const
IntConstraints& system_t
// We will generate formulas of the form coef_lcm*v <= bound
int64_t coef_lcm = 1;
for (const auto& pos : coef_pos) {
- coef_lcm = LeastCommonMultiple(coef_lcm, pos.first);
+ coef_lcm = std::lcm(coef_lcm, pos.first);
}
for (const auto& neg : coef_neg) {
- coef_lcm = LeastCommonMultiple(coef_lcm, -neg.first);
+ coef_lcm = std::lcm(coef_lcm, -neg.first);
}
// The resulting lower and upper bounds
diff --git a/src/s_tir/schedule/primitive/compute_at.cc
b/src/s_tir/schedule/primitive/compute_at.cc
index 6f7439ede9..89abb7330c 100644
--- a/src/s_tir/schedule/primitive/compute_at.cc
+++ b/src/s_tir/schedule/primitive/compute_at.cc
@@ -438,23 +438,29 @@ std::pair<Var, BlockVarDomainInfo>
SolveBlockVarDomain(const sym::IntSet& provid
PrimExpr required_max = analyzer->Simplify(required.max());
sym::IntSet var_dom{ffi::UnsafeInit{}}, var_bound{ffi::UnsafeInit{}};
ffi::Optional<Var> var;
- sym::PVar<Var> p_v;
- sym::PVar<PrimExpr> p_e;
- if ((p_v * p_e).Match(provided_min) || (p_e * p_v).Match(provided_min)) {
- PrimExpr e = p_e.Eval();
- var = p_v.Eval();
+ ffi::Optional<PrimExpr> factor;
+ if (const auto* mul = provided_min.as<prim::MulNode>()) {
+ if (auto lhs = mul->a.as<Var>()) {
+ var = lhs;
+ factor = mul->b;
+ } else if (auto rhs = mul->b.as<Var>()) {
+ var = rhs;
+ factor = mul->a;
+ }
+ }
+ if (factor.has_value()) {
+ PrimExpr e = factor.value();
var_dom = sym::IntSet::Interval(floordiv(required_min, e),
floordiv(required_max, e));
var_bound = sym::IntSet::Interval(0, floordiv(dim_max, e));
} else if (analyzer->CanProveEqual(provided_min, provided_max)) {
- if (p_v.Match(provided_min)) {
- var = p_v.Eval();
+ if (auto single_var = provided_min.as<Var>()) {
+ var = single_var;
var_dom = sym::IntSet::Interval(required_min, required_max);
var_bound = sym::IntSet::Interval(0, dim_max);
} else {
- sym::PVar<PrimExpr> p_f1, p_f2;
- if ((floordiv(p_f1, p_f2).Match(provided_min))) {
- PrimExpr var_expr = p_f1.Eval();
- PrimExpr fac = p_f2.Eval();
+ if (const auto* div = provided_min.as<prim::FloorDivNode>()) {
+ PrimExpr var_expr = div->a;
+ PrimExpr fac = div->b;
if (analyzer->CanProveGreaterEqual(fac, 1)) {
if (var_expr.as<PrimVar>()) {
// a <= (x // factor) <= b, fac > 0 ==> (a * fac) <= x <= (b * fac
+ fac - 1)
@@ -463,27 +469,27 @@ std::pair<Var, BlockVarDomainInfo>
SolveBlockVarDomain(const sym::IntSet& provid
analyzer->Simplify(required_max *
fac + fac - 1));
var_bound = sym::IntSet::Interval(0, analyzer->Simplify(dim_max *
fac + fac - 1));
} else {
- const sym::IntSet new_provided =
sym::IntSet::SinglePoint(p_f1.Eval());
+ const sym::IntSet new_provided = sym::IntSet::SinglePoint(div->a);
const sym::IntSet new_required = sym::IntSet::Interval(
required_min * fac, analyzer->Simplify(required_max * fac +
fac - 1));
return SolveBlockVarDomain(new_provided, new_required, dim_max,
analyzer);
}
}
- } else if ((floormod(p_f1, p_f2).Match(provided_min))) {
- PrimExpr var_expr = p_f1.Eval();
+ } else if (const auto* mod = provided_min.as<prim::FloorModNode>()) {
+ PrimExpr var_expr = mod->a;
if (var_expr.as<PrimVar>()) {
// generally domain of (x % fac) enforce no constraints to domain of
x
Var var_mod = var_expr.as_or_throw<Var>();
return {var_mod, BlockVarDomainInfo()};
} else {
- PrimExpr mod_1 = p_f1.Eval();
- PrimExpr mod_2 = p_f2.Eval();
+ PrimExpr mod_1 = mod->a;
+ PrimExpr mod_2 = mod->b;
if (analyzer->CanProveGreaterEqual(mod_1, 1) &&
analyzer->CanProveGreaterEqual(mod_2, 1)) {
- const sym::IntSet new_provided =
sym::IntSet::SinglePoint(p_f1.Eval());
+ const sym::IntSet new_provided = sym::IntSet::SinglePoint(mod->a);
if (analyzer->CanProveGreaterEqual(required_min, 0)) {
const sym::IntSet new_required =
- sym::IntSet::Interval(required_min,
sym::SymbolicLimits::pos_inf_);
+ sym::IntSet::Interval(required_min,
sym::IntSet::Everything().max());
return SolveBlockVarDomain(new_provided, new_required, dim_max,
analyzer);
}
}
diff --git a/src/s_tir/schedule/primitive/decompose_padding.cc
b/src/s_tir/schedule/primitive/decompose_padding.cc
index bf6a37ca62..544fd875c1 100644
--- a/src/s_tir/schedule/primitive/decompose_padding.cc
+++ b/src/s_tir/schedule/primitive/decompose_padding.cc
@@ -153,10 +153,9 @@ class PaddingInfoAnalyzer {
PrimExpr RewritePredicate(const PrimExpr& predicate) {
PrimExpr res = IntImm::Bool(true);
std::function<void(PrimExpr)> update = [&res, &update](PrimExpr e) {
- sym::PVar<PrimExpr> a, b;
- if ((a && b).Match(e)) {
- update(a.Eval());
- update(b.Eval());
+ if (const auto* and_node = e.as<prim::AndNode>()) {
+ update(and_node->a);
+ update(and_node->b);
} else {
if (const CallNode* call = e.as<CallNode>()) {
if (call->op.same_as(prim::likely_op())) {
diff --git a/src/s_tir/schedule/primitive/rolling_buffer.cc
b/src/s_tir/schedule/primitive/rolling_buffer.cc
index aaf8acc57f..9e4b5cf9b2 100644
--- a/src/s_tir/schedule/primitive/rolling_buffer.cc
+++ b/src/s_tir/schedule/primitive/rolling_buffer.cc
@@ -191,32 +191,39 @@ class RollingBufferInfoCollector {
std::vector<ffi::Optional<Var>> bound_iter_vars;
std::vector<int> bound_overlaps;
- sym::PVar<Var> p_var;
- sym::PVar<IntImm> p_stride, p_divisor;
for (auto bound : region) {
auto stride = 0;
auto divisor = 1;
ffi::Optional<Var> iter_var;
- if (floordiv((p_var * p_stride), p_divisor).Match(bound->min)) {
+ if (const auto* div = bound->min.as<prim::FloorDivNode>()) {
+ const auto* mul = div->a.as<prim::MulNode>();
+ if (!mul) return false;
+ iter_var = mul->a.as<Var>();
+ auto stride_imm = mul->b.as<IntImm>();
+ auto divisor_imm = div->b.as<IntImm>();
+ if (!iter_var.has_value() || !stride_imm.has_value() ||
!divisor_imm.has_value()) {
+ return false;
+ }
// Handle the case of fractional strides
- // They take this form: floordiv(hh.outer, 2)
+ // They take this form: floordiv(hh.outer * stride, divisor)
// Strip the floordiv and keep track of the divisor
- iter_var = p_var.Eval();
- auto divisor_value = p_divisor.Eval()->value.as<int>();
- auto stride_value = p_stride.Eval()->value.as<int>();
+ auto divisor_value = divisor_imm.value()->value.as<int>();
+ auto stride_value = stride_imm.value()->value.as<int>();
if (!divisor_value.has_value() || !stride_value.has_value()) return
false;
divisor = *divisor_value;
stride = std::ceil(static_cast<float>(*stride_value) / divisor);
- } else if ((p_var * p_stride).Match(bound->min)) {
+ } else if (const auto* mul = bound->min.as<prim::MulNode>()) {
// The bound is the iter var multiplied by the stride
- iter_var = p_var.Eval();
- auto stride_value = p_stride.Eval()->value.as<int>();
+ iter_var = mul->a.as<Var>();
+ auto stride_imm = mul->b.as<IntImm>();
+ if (!iter_var.has_value() || !stride_imm.has_value()) return false;
+ auto stride_value = stride_imm.value()->value.as<int>();
if (!stride_value.has_value()) return false;
stride = *stride_value;
- } else if (p_var.Match(bound->min)) {
+ } else if (auto var = bound->min.as<Var>()) {
// If the bound is just a Var, that implies the stride is 1
- iter_var = p_var.Eval();
+ iter_var = var;
stride = 1;
} else if (IsConstInt(bound->min)) {
// If the bound is an int, we can't roll over it
diff --git a/src/s_tir/schedule/utils.h b/src/s_tir/schedule/utils.h
index b6fe4b6342..745d5709f1 100644
--- a/src/s_tir/schedule/utils.h
+++ b/src/s_tir/schedule/utils.h
@@ -45,7 +45,6 @@
#include <utility>
#include "../../runtime/thread_storage_scope.h"
-#include "../../sym/pattern_match.h"
#include "../support/array_utils.h"
#include "../support/nd_int_set.h"
#include "./analysis.h"
@@ -212,18 +211,29 @@ inline ffi::Optional<Var> AnalyzeVarWithShift(const
PrimExpr& expr,
*constant = std::nullopt;
return static_cast<Var>(var.value());
}
- sym::PVar<Var> var;
- sym::PVar<IntImm> shift;
// match: "var + shift"
- if ((var + shift).Match(expr) || (shift + var).Match(expr)) {
- *constant = shift.Eval();
- return var.Eval();
+ if (const auto* add = expr.as<prim::AddNode>()) {
+ if (auto var = add->a.as<Var>()) {
+ if (auto shift = add->b.as<IntImm>()) {
+ *constant = shift;
+ return var;
+ }
+ }
+ if (auto shift = add->a.as<IntImm>()) {
+ if (auto var = add->b.as<Var>()) {
+ *constant = shift;
+ return var;
+ }
+ }
}
// match: "var - shift"
- if ((var - shift).Match(expr)) {
- IntImm result = shift.Eval();
- *constant = IntImm(result.ty(), -result->value);
- return var.Eval();
+ if (const auto* sub = expr.as<prim::SubNode>()) {
+ if (auto var = sub->a.as<Var>()) {
+ if (auto shift = sub->b.as<IntImm>()) {
+ *constant = IntImm(shift.value().ty(), -shift.value()->value);
+ return var;
+ }
+ }
}
return std::nullopt;
}
diff --git a/src/s_tir/transform/compact_tensor_region.cc
b/src/s_tir/transform/compact_tensor_region.cc
index ad30315c40..c45dbc94ff 100644
--- a/src/s_tir/transform/compact_tensor_region.cc
+++ b/src/s_tir/transform/compact_tensor_region.cc
@@ -134,6 +134,12 @@ class TensorAccessRegionCollector : public StmtExprVisitor
{
}
private:
+ static bool IsIndexTypedExpr(const PrimExpr& expr) {
+ PrimType type = expr.ty();
+ return type.IsScalar() && type.MatchesCode(DLDataTypeCode::kDLInt) &&
+ (type.bits() == 32 || type.bits() == 64);
+ }
+
struct TensorAccessInfo {
/*! \brief The tensor. */
TensorVar tensor;
@@ -212,7 +218,7 @@ class TensorAccessRegionCollector : public StmtExprVisitor {
call && call->op.same_as(tirx::decl_tensor_op()))
return StmtExprVisitor::Visit_(op);
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(StmtExprVisitor::Visit(op->value));
- if (auto value = op->value.as<PrimExpr>(); value &&
sym::IsIndexTypedExpr(value.value())) {
+ if (auto value = op->value.as<PrimExpr>(); value &&
IsIndexTypedExpr(value.value())) {
dom_analyzer_->Bind(op->var, value.value());
dom_map_.emplace(op->var.get(), sym::IntSet::SinglePoint(value.value()));
}
@@ -221,12 +227,12 @@ class TensorAccessRegionCollector : public
StmtExprVisitor {
ffi::Optional<VisitInterrupt> Visit_(const LetNode* op) final {
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(StmtExprVisitor::Visit(op->value));
- if (sym::IsIndexTypedExpr(op->value)) {
+ if (IsIndexTypedExpr(op->value)) {
dom_analyzer_->Bind(op->var, op->value);
dom_map_.emplace(op->var.get(), sym::IntSet::SinglePoint(op->value));
}
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(StmtExprVisitor::Visit(op->body));
- if (sym::IsIndexTypedExpr(op->value)) {
+ if (IsIndexTypedExpr(op->value)) {
dom_map_.erase(op->var.get());
}
return std::nullopt;
diff --git a/src/s_tir/transform/loop_partition.cc
b/src/s_tir/transform/loop_partition.cc
index 23273ab176..18e260b805 100644
--- a/src/s_tir/transform/loop_partition.cc
+++ b/src/s_tir/transform/loop_partition.cc
@@ -44,7 +44,6 @@
#include <unordered_map>
#include <unordered_set>
-#include "../../sym/interval_set.h"
#include "ir_utils.h"
namespace tvm {
@@ -586,7 +585,7 @@ class LoopPartitioner : public StmtExprMutator {
bool partition_thread_scope);
std::pair<IntSet, ExpressionSet> GetIntervalAndCondset(const Partition&
partitions,
- const
sym::IntervalSet& for_interval,
+ const IntSet&
for_interval,
bool cond_value, bool
has_partition_hint);
inline Stmt MakeFor(const ffi::Object* op, PrimExpr extent, Stmt body);
@@ -602,18 +601,19 @@ class LoopPartitioner : public StmtExprMutator {
// Returns an interval (in the first component) in which all the conditions
// given in the second component provably have value given by cond_value
-std::pair<IntSet, ExpressionSet> LoopPartitioner::GetIntervalAndCondset(
- const Partition& partitions, const sym::IntervalSet& for_interval, bool
cond_value,
- bool has_partition_hint) {
+std::pair<IntSet, ExpressionSet> LoopPartitioner::GetIntervalAndCondset(const
Partition& partitions,
+ const
IntSet& for_interval,
+ bool
cond_value,
+ bool
has_partition_hint) {
ffi::Array<IntSet> sets;
ExpressionSet cond_set;
for (const auto& kv : partitions) {
if (kv.first.second == cond_value) {
- sym::IntervalSet interval = kv.second.as_or_throw<sym::IntervalSet>();
- sym::IntervalSet intersection = sym::Intersect(analyzer_.get(),
interval, for_interval);
+ const IntSet& interval = kv.second;
+ IntSet intersection = sym::Intersect(interval, for_interval, analyzer_);
- if (!intersection->IsEmpty()) {
+ if (!intersection.IsNothing()) {
sets.push_back(kv.second);
cond_set.insert(kv.first.first);
}
@@ -624,21 +624,20 @@ std::pair<IntSet, ExpressionSet>
LoopPartitioner::GetIntervalAndCondset(
// Try to find the intersection of the cond_intervals until the intersection
// is nothing when has_partition_hint is true.
if (interval.IsNothing() && has_partition_hint) {
- sym::IntervalSet cond_intersection = sym::IntervalSet::Everything();
+ IntSet cond_intersection = IntSet::Everything();
cond_set.clear();
for (const auto& kv : partitions) {
if (kv.first.second == cond_value) {
- sym::IntervalSet cond_interval =
kv.second.as_or_throw<sym::IntervalSet>();
- sym::IntervalSet intersection =
- sym::Intersect(analyzer_.get(), cond_interval, for_interval);
- if (!intersection->IsEmpty()) {
- cond_intersection = sym::Intersect(analyzer_.get(),
cond_intersection, cond_interval);
+ const IntSet& cond_interval = kv.second;
+ IntSet intersection = sym::Intersect(cond_interval, for_interval,
analyzer_);
+ if (!intersection.IsNothing()) {
+ cond_intersection = sym::Intersect(cond_intersection, cond_interval,
analyzer_);
// Return the latest interval and cond_set if the cond_intersection
is nothing.
- if (!cond_intersection->IsEmpty()) {
+ if (!cond_intersection.IsNothing()) {
cond_set.insert(kv.first.first);
- interval =
sym::IntervalSet(analyzer_->Simplify(cond_intersection->min_value),
-
analyzer_->Simplify(cond_intersection->max_value));
+ interval =
IntSet::Interval(analyzer_->Simplify(cond_intersection.min()),
+
analyzer_->Simplify(cond_intersection.max()));
} else {
break;
}
@@ -711,7 +710,7 @@ ffi::Optional<Stmt> LoopPartitioner::TryPartition(const
Stmt& stmt, Var var, Pri
hint_map_.erase(var.get());
if (finder->partitions.empty()) return std::nullopt;
- sym::IntervalSet for_interval(min, max);
+ IntSet for_interval = IntSet::Interval(min, max);
auto [middle_interval, cond_set, opt_cond_value] =
[&]() -> std::tuple<ffi::Optional<IntSet>, ExpressionSet,
std::optional<bool>> {
@@ -788,7 +787,6 @@ ffi::Optional<Stmt> LoopPartitioner::TryPartition(const
Stmt& stmt, Var var, Pri
}
bool cond_value = opt_cond_value.value();
- IntervalSet middle_interval_i =
middle_interval.value().as_or_throw<IntervalSet>();
// middle_interval is the subrange of the loop variable range for which a
// set of conditions are true (or false resp.)
// The part of the loop variable range that is before (after resp.) that
@@ -799,7 +797,7 @@ ffi::Optional<Stmt> LoopPartitioner::TryPartition(const
Stmt& stmt, Var var, Pri
PrimExpr body_begin = min;
ffi::Optional<Stmt> pre_stmt;
bool pre_stmt_recurse = true;
- if (middle_interval_i->HasLowerBound()) {
+ if (middle_interval.value().HasLowerBound()) {
body_begin = analyzer_->Simplify(middle_interval.value().min());
if (!analyzer_->CanProve(body_begin == min)) {
PrimExpr extent = analyzer_->Simplify(body_begin - min);
@@ -832,7 +830,7 @@ ffi::Optional<Stmt> LoopPartitioner::TryPartition(const
Stmt& stmt, Var var, Pri
PrimExpr post_doubt_begin = max + 1;
ffi::Optional<Stmt> post_stmt;
bool post_stmt_recurse = true;
- if (middle_interval_i->HasUpperBound()) {
+ if (middle_interval.value().HasUpperBound()) {
post_doubt_begin = analyzer_->Simplify(middle_interval.value().max() + 1);
if (!analyzer_->CanProve(middle_interval.value().max() == max)) {
// require the extent to be non-negative
diff --git a/src/s_tir/transform/using_assume_to_reduce_branches.cc
b/src/s_tir/transform/using_assume_to_reduce_branches.cc
deleted file mode 100644
index 7f083e21f9..0000000000
--- a/src/s_tir/transform/using_assume_to_reduce_branches.cc
+++ /dev/null
@@ -1,422 +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 using_assume_to_reduce_branches.cc
- *
- * \brief Attempt to remove conditional branch statements by introducing
- * extra computations that do not impact the final results. Mainly
- * oriented for layout specific padding related branches.
- *
- * \note
- * 1. This pass works if the tensor assumption variable is in the branch
statement.
- * In case, the tensor assumption is not present in the branch statement
and
- * there are intermediate tensors then, inline the code.
- * 2. The assumptions leveraged here should be of the form
T.assume(condition_on_indices or
- * tensor_equals_to_some_value)
- * 3. Some part of the code are reused from the control_flow_graph.cc file
which also
- * handles eliminating branches in particular scenarios.
- * 4. This pass currently works for op_pattern kElemWise and kBroadcast.
- */
-
-#include <tvm/ffi/cast.h>
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/ir/prim/op.h>
-#include <tvm/relax/expr.h>
-#include <tvm/relax/op_attr_types.h>
-#include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/stmt_functor_with_analyzer.h>
-#include <tvm/s_tir/transform.h>
-#include <tvm/tirx/function.h>
-
-#include <unordered_map>
-
-#include "../../sym/constraint_extract.h"
-#include "tvm/ir/expr.h"
-namespace tvm {
-namespace s_tir {
-using namespace tvm::tirx;
-
-using namespace sym;
-
-class AssumeChecker : public StmtExprVisitor {
- public:
- using StmtExprVisitor::Visit_;
-
- /* This class checks if the function has assume statement.
- If yes, then only the FuncAnanlyzerMutator class runs. This is to ensure
speedup in the pass.*/
- bool has_assume = false;
-
- ffi::Optional<VisitInterrupt> Visit(ffi::AnyView stmt) final {
- if (has_assume) {
- return std::nullopt;
- }
- return StmtExprVisitor::Visit(stmt);
- }
- ffi::Optional<VisitInterrupt> Visit_(const CallNode* op) override {
- if (op->op.same_as(prim::assume_op())) {
- has_assume = true;
- }
- return std::nullopt;
- }
-};
-
-class ParseAssumeAndOvercompute : public StmtExprMutatorWithAnalyzer {
- public:
- using StmtExprMutatorWithAnalyzer::Mutate;
- using StmtExprMutatorWithAnalyzer::Mutate_;
-
- /* This class analyzes the complete function.
- It parses the tensor assumptions and eliminates the redundant branch
- introduced due to layout specific padding by leveraging from tensor
assumptions.
- On eliminating the branch there are more opportunities to vectorize the code
- and improve performance.
-
- Example:
- -------------
- Function Before :
- for (...)
- T.assume( assume_condition or A[i] == 0 )
- for (...)
- out = T.if_then_else(if_then_else_condition, 0, function(A))
- # here function(A) is some function on Var A
-
- Function After :
- for (...)
- T.assume( assume_condition or A[i] == 0 )
- for (...)
- out = function(A) # here function(A) is some function on the Var A
- --------------
- # High-level implementation details :
- 1. The pass parses the assume statement and stores the relevant
information.
- 2. The pass tries to evaluate the then_clause and else_clause in
then_condition_context
- and else_condition_context.
- It checks if the context of the assume statement (for condition indices and
- assume_condition) is same as the context of the if_then_else statement
(for condition indices
- and if_then_else condition). If context is same and the expression inside
if_then_else statement
- is a function of the tensor assumption (eg A in above example),
- then the pass substitutes the value from the tensor assumption and
simplifies the expression.
- 3. The pass then checks if then_clause and else_clause evaluate to same
value.
- If yes, then return the else_clause if we are in the
then_condition_context (since then_clause
- will be true in this context and if else_clause is also evaluating to true
then we can directly
- replace it with else_clause), similarly, we return the then_clause if we
are in the
- else_condition_context.
- This class handles all these scenarios.*/
-
- using Parent = StmtExprMutatorWithAnalyzer;
- explicit ParseAssumeAndOvercompute(const Analyzer& analyzer) :
Parent(analyzer) {}
-
- private:
- // This struct stores all the relevant data related to asssume statement
- struct assume_struct { // Consider the example : T.assume(i < 14 or
A[i] == 0)
- PrimExpr tensor_context; // The context of the assume statement (the
bound on the axis)
- PrimExpr tensor_predicate; // The condition inside assume statement (i <
14) excluding
- // tensorload expression (A[i] == 0)
- TensorLoad tensor_load; // Storing the tensor load Eg: A[i] in A[i] == 0
- PrimExpr tensor_value; // Storing the value for the tensor Eg : 0 in
A[i] == 0
- ffi::Array<PrimExpr> tensor_indices; // Storing the indices of the tensor
Eg : i
- };
- // List of conditions in a scope
- std::vector<PrimExpr> conditions_;
-
- // Storing all the tensor assumptions data in map
- std::unordered_map<tirx::TensorVar, assume_struct, ffi::ObjectPtrHash,
ffi::ObjectPtrEqual>
- map_tensor_assumption;
-
- struct InternalConstraintContext {
- /* This stuct appends the constraint passed to it in the conditions list.
- It keeps track of the bounds of the variables along with any conditions on
the variables */
- InternalConstraintContext(ParseAssumeAndOvercompute* self, PrimExpr
constraint)
- : self(self), analyzer_context(self->analyzer_, constraint) {
- old_num_constraints = self->conditions_.size();
-
- auto side_effect = SideEffect(constraint);
- if (side_effect <= CallEffectKind::kPure) {
- self->conditions_.push_back(constraint);
- } else if (side_effect <= CallEffectKind::kReadState) {
- assume = constraint;
- }
-
- new_num_constraints = self->conditions_.size();
- }
-
- ~InternalConstraintContext() {
- TVM_FFI_ICHECK_EQ(self->conditions_.size(), new_num_constraints)
- << "Internal error: Each condition should only be popped once.";
- self->conditions_.erase(self->conditions_.begin() + old_num_constraints,
- self->conditions_.end());
- }
-
- ParseAssumeAndOvercompute* self{nullptr};
- With<sym::ConstraintContext> analyzer_context;
- size_t old_num_constraints{0};
- size_t new_num_constraints{0};
- ffi::Optional<PrimExpr> assume{std::nullopt};
-
- // Disable default-generated copy/move assignment and constructors
- InternalConstraintContext(const InternalConstraintContext&) = delete;
- InternalConstraintContext& operator=(const InternalConstraintContext&) =
delete;
- InternalConstraintContext(InternalConstraintContext&&) = delete;
- InternalConstraintContext& operator=(InternalConstraintContext&&) = delete;
- };
-
- PrimExpr CurrentScopePredicate() const {
- /* This combines all the constraints in a scope */
- PrimExpr predicate = IntImm::Bool(true);
- for (const auto& condition : conditions_) {
- predicate = predicate && condition;
- }
- return predicate;
- }
-
- UnchangedOr<Stmt> Mutate_(const ForNode* op, InplaceMode inplace_mode) final
{
- /* Create and delete the scope with bind.
- Add the minimum and maximum bound for the variables to the conditions_
list using
- InternalConstraintContext */
- analyzer_->Bind(op->loop_var, Range::FromMinExtent(op->min, op->extent));
- InternalConstraintContext ctx1(this, op->loop_var >= op->min);
- InternalConstraintContext ctx2(this,
- static_cast<PrimExpr>(op->loop_var) <
op->min + op->extent);
- return Parent::Mutate_(op, inplace_mode);
- }
-
- UnchangedOr<PrimExpr> Mutate_(const TensorLoadNode* op, InplaceMode
inplace_mode) override {
- if
(map_tensor_assumption.find(op->source.as_or_throw<tvm::tirx::TensorVar>()) !=
- map_tensor_assumption.end()) {
- /* If the cuurent context where the tensor load is present is same as
- the context of the tensor assumption then, return the tensor value
present in the assumption.
- This will eventually replace the tensorload value in the complete
expresison */
-
- auto tensor_assumption =
-
map_tensor_assumption.at(op->source.as_or_throw<tvm::tirx::TensorVar>());
- PrimExpr current_predicate_and_context = CurrentScopePredicate();
- PrimExpr tensor_predicate_and_context =
- tensor_assumption.tensor_context &&
tensor_assumption.tensor_predicate;
- bool current_context_and_tensor_constraint_is_same =
ffi::StructuralEqual::Equal(
- current_predicate_and_context, tensor_predicate_and_context,
/*map_free_vars=*/true);
-
- if (current_context_and_tensor_constraint_is_same) {
- return tensor_assumption.tensor_value;
- }
- }
- return ffi::Unchanged();
- }
-
- UnchangedOr<Stmt> Mutate_(const TensorStoreNode* op, InplaceMode
inplace_mode) final {
- TensorStore store = Parent::Mutate_(op, inplace_mode)
- .ValueOrUnchanged(ffi::GetRef<Stmt>(op))
- .as_or_throw<TensorStore>();
- if (!op->unique()) inplace_mode = InplaceMode::kDisallow;
-
- // Eliminate the builtin if_then_else statement
- if (auto* call = op->value.as<CallNode>()) {
- if (call->op.same_as(prim::if_then_else_op())) {
- PrimExpr cond = call->args[0].as_or_throw<PrimExpr>();
- PrimExpr then_clause = call->args[1].as_or_throw<PrimExpr>();
- PrimExpr else_clause = call->args[2].as_or_throw<PrimExpr>();
-
- PrimExpr then_clause_in_then_context{ffi::UnsafeInit{}};
- PrimExpr else_clause_in_then_context{ffi::UnsafeInit{}};
- PrimExpr then_clause_in_else_context{ffi::UnsafeInit{}};
- PrimExpr else_clause_in_else_context{ffi::UnsafeInit{}};
- {
- // Simplifying expressions in " then context "
- InternalConstraintContext then_ctx(this, cond);
- // This will call the current class's appropriate VisitStmt function
- then_clause_in_then_context =
- Mutate(then_clause, inplace_mode).ValueOrUnchanged(then_clause);
- then_clause_in_then_context =
analyzer_->Simplify(then_clause_in_then_context);
-
- else_clause_in_then_context =
- Mutate(else_clause, inplace_mode).ValueOrUnchanged(else_clause);
- else_clause_in_then_context =
analyzer_->Simplify(else_clause_in_then_context);
- }
- {
- // Simplifying expressions in " else context "
- InternalConstraintContext else_ctx(this, !cond);
- // This will call the current class's appropriate VisitStmt function
- then_clause_in_else_context =
- Mutate(then_clause, inplace_mode).ValueOrUnchanged(then_clause);
- then_clause_in_else_context =
analyzer_->Simplify(then_clause_in_else_context);
-
- else_clause_in_else_context =
- Mutate(else_clause, inplace_mode).ValueOrUnchanged(else_clause);
- else_clause_in_else_context =
analyzer_->Simplify(else_clause_in_else_context);
- }
-
- if (ffi::StructuralEqual()(then_clause_in_then_context,
else_clause_in_then_context)) {
- PrimExpr value = analyzer_->Simplify(else_clause);
- if (inplace_mode == InplaceMode::kAllow) {
- const_cast<TensorStoreNode*>(op)->value = std::move(value);
- return ffi::Unchanged();
- } else {
- auto copy = ffi::make_object<TensorStoreNode>(*op);
- copy->value = std::move(value);
- return TensorStore(std::move(copy));
- }
- } else if (ffi::StructuralEqual()(then_clause_in_else_context,
- else_clause_in_else_context)) {
- PrimExpr value = analyzer_->Simplify(then_clause);
- if (inplace_mode == InplaceMode::kAllow) {
- const_cast<TensorStoreNode*>(op)->value = std::move(value);
- return ffi::Unchanged();
- } else {
- auto copy = ffi::make_object<TensorStoreNode>(*op);
- copy->value = std::move(value);
- return TensorStore(std::move(copy));
- }
- } else {
- return Parent::Mutate_(op, inplace_mode);
- }
- }
- }
- return Parent::Mutate_(op, inplace_mode);
- }
-
- UnchangedOr<Expr> Mutate_(const CallNode* op, InplaceMode inplace_mode)
override {
- if (op->op.same_as(prim::assume_op())) {
- Assume(op->args[0].as_or_throw<PrimExpr>());
- }
- return Parent::Mutate_(op, inplace_mode);
- }
-
- void Assume(PrimExpr assumption) {
- for (const auto& expr : sym::ExtractConstraints(assumption, false)) {
- AssumeConstraintComponent(expr);
- }
- }
-
- void AssumeConstraintComponent(PrimExpr assumption) {
- PrimExpr additional_predicate = IntImm::Bool(true);
-
- std::vector<PrimExpr> tensor_exprs;
- for (const auto& expr : sym::ExtractComponents(assumption)) {
- auto side_effect = SideEffect(expr);
- if (side_effect <= CallEffectKind::kPure) {
- // Pulling out portions of the assumption that do not depend
- // on a tensor value allows the following two forms to be
- // treated identically.
- //
- // Option 1: if i < 3: T.assume(tensor[i] == value)
- // Option 2: T.assume(i>=3 or tensor[i] == value)
- additional_predicate = additional_predicate && logical_not(expr);
- } else if (side_effect == CallEffectKind::kReadState) {
- tensor_exprs.push_back(expr);
- } else {
- TVM_FFI_THROW(InternalError)
- << "Assumption must be pure or read-only, but contained expression
" << expr
- << " with side-effect \'" << side_effect << "\'";
- }
- }
-
- additional_predicate =
analyzer_->Simplify(std::move(additional_predicate));
- TVM_FFI_ICHECK_EQ(tensor_exprs.size(), 1)
- << "T.assume must contain only a single tensor expression";
-
- auto* as_equal_node = tensor_exprs[0].as<prim::EQNode>();
- TVM_FFI_ICHECK(as_equal_node)
- << "T.assume tensor constraint must be of the form 'tensor[indices] ==
"
- "value', but received "
- << assumption;
- if (!as_equal_node) {
- // This assumption is an inequality on a data-dependent
- // conditional. Not an error for this to occur, but also not
- // something that is currently supported.
- return;
- }
-
- // Parse the statement and store the desired values
- // Ex: A[i]==0, load = A[i], value = 0
- TensorLoad load{ffi::UnsafeInit{}};
- PrimExpr value{ffi::UnsafeInit{}};
- if (auto opt = as_equal_node->a.as<TensorLoad>()) {
- load = opt.value();
- value = as_equal_node->b;
- } else if (auto opt = as_equal_node->b.as<TensorLoad>()) {
- load = opt.value();
- value = as_equal_node->a;
- } else {
- TVM_FFI_THROW(InternalError)
- << "T.assume tensor constraint must be of the form 'tensor[indices]
== value'";
- }
-
- // Populating the assume statement predicate, tensor, value
- // and the context of the assume statement
- assume_struct tensor_data{CurrentScopePredicate(), additional_predicate,
load, value,
- load->indices};
- for (size_t i = 0; i < load->indices.size(); i++) {
-
tensor_data.tensor_indices.push_back(analyzer_->Simplify(load->indices[i]));
- }
- map_tensor_assumption.insert_or_assign(
- tensor_data.tensor_load->source.as_or_throw<tvm::tirx::TensorVar>(),
tensor_data);
-
- auto has_side_effect = SideEffect(value) > CallEffectKind::kPure;
- TVM_FFI_ICHECK(!has_side_effect)
- << "TensorVar value in constraint must be pure expression, but was "
<< value;
- if (has_side_effect) {
- return;
- }
- }
-};
-
-namespace transform {
-
-Pass UseAssumeToReduceBranches() {
- auto pass_func = [](Function f, IRModule m, PassContext ctx) {
- auto* n = f.CopyOnWrite();
- sym::Analyzer analyzer;
-
- // The pass runs & eliminates pad branch with overcompute only if,
- // the function has op_pattern defined and is an elementwise op.
- // AnnotateTIROpPattern pass will set op_pattern in op attributes of the
function.
- if (n->attrs.GetAttr<int64_t>(tvm::relax::attr::kOpPattern).has_value()) {
- ffi::Optional<int64_t> opt_pattern =
f->GetAttr<int64_t>(tvm::relax::attr::kOpPattern);
- if (opt_pattern.has_value()) {
- relax::OpPatternKind pattern;
- pattern = static_cast<relax::OpPatternKind>(opt_pattern.value());
-
- if (pattern == relax::OpPatternKind::kElemWise ||
- pattern == relax::OpPatternKind::kBroadcast) {
- // If the function contains assume statement then, run the mutator
pass.
- auto assume_checker = ffi::make_object<AssumeChecker>();
- assume_checker->Visit(std::move(n->body));
-
- if (assume_checker->has_assume) {
- // Leverage from assume and eliminate the branch
- auto func_analyzer_mutator =
ffi::make_object<ParseAssumeAndOvercompute>(analyzer);
- n->body = func_analyzer_mutator->Mutate(n->body,
InplaceMode::kAllow)
- .ValueOrUnchanged(std::move(n->body));
- }
- }
- }
- }
- return f;
- };
- return CreateFunctionPass(pass_func, 0, "s_tir.UseAssumeToReduceBranches");
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
- namespace refl = tvm::ffi::reflection;
- refl::GlobalDef().def("s_tir.transform.UseAssumeToReduceBranches",
UseAssumeToReduceBranches);
-}
-
-} // namespace transform
-
-} // namespace s_tir
-} // namespace tvm
diff --git a/src/sym/const_int_bound.cc b/src/sym/const_int_bound.cc
index 7a9f5fae01..85f5078612 100644
--- a/src/sym/const_int_bound.cc
+++ b/src/sym/const_int_bound.cc
@@ -30,6 +30,7 @@
#include <tvm/sym/analyzer.h>
#include <algorithm>
+#include <numeric>
#include <optional>
#include "constraint_extract.h"
@@ -308,7 +309,7 @@ class ConstIntBoundAnalyzer::Impl
if (parent_ && b.min_value == b.max_value) {
ModularSet mod_a = parent_->modular_set(op->a);
int64_t modulus = b.min_value;
- int64_t gcd_coeff_mod = ZeroAwareGCD(mod_a->coeff, modulus);
+ int64_t gcd_coeff_mod = std::gcd(mod_a->coeff, modulus);
// If gcd_coeff_mod > 1, we can get tighter bounds.
// Since gcd_coeff_mod divides both mod_a->coeff and modulus, we know
@@ -409,7 +410,7 @@ class ConstIntBoundAnalyzer::Impl
if (parent_ && b.min_value == b.max_value) {
ModularSet mod_a = parent_->modular_set(op->a);
int64_t modulus = b.min_value;
- int64_t gcd_coeff_mod = ZeroAwareGCD(mod_a->coeff, modulus);
+ int64_t gcd_coeff_mod = std::gcd(mod_a->coeff, modulus);
// If gcd_coeff_mod > 1, we can get tighter bounds.
// Since gcd_coeff_mod divides both mod_a->coeff and modulus, we know
diff --git a/src/sym/int_operator.h b/src/sym/int_operator.h
index e9e6c33073..92d4597a99 100644
--- a/src/sym/int_operator.h
+++ b/src/sym/int_operator.h
@@ -28,6 +28,7 @@
#include <tvm/ir/prim/expr.h>
#include <limits>
+#include <numeric>
#include <utility>
#include "../ir/prim/int_operator.h"
@@ -91,78 +92,20 @@ using prim::detail::floormod;
using prim::detail::truncdiv;
using prim::detail::truncmod;
-/*!
- * \brief Use Extended Euclidean algorithm to solve ax + by = gcd(a, b)
- * \param a The first coefficient.
- * \param b The second coefficient.
- * \param x The solution of x.
- * \param y The solution of y.
- * \return The GCD of a and b.
- */
-inline int64_t ExtendedEuclidean(int64_t a, int64_t b, int64_t* x, int64_t* y)
{
- // Extended Euclidean algorithm
- // if a < 0, the problem can be convert into
- // |a|* (-x) + b * y = gcd(|a|, b)
- //
- // initial condition:
- // a * 0 + b * 1 = b
- // a * 1 + b * 0 = a
- int64_t s = 0, old_s = 1;
- int64_t r = b, old_r = a >= 0 ? a : -a;
- // Iteration (r2 < r1):
- // a * x1 + b * y1 = r1
- // a * x2 + b * y2 = r2
- // The above two eqs can derive the following eq (q = r1 / r2)
- // a * (x1 - x2 * q) + b * (y1 - y2 * q) = r1 - r2 * q = r3
- // Because r3 < r2, the iteration can eventually terminate
- while (r != 0) {
- int64_t q = old_r / r;
- int64_t tmp = old_r;
- old_r = r;
- r = tmp - q * r;
- tmp = old_s;
- old_s = s;
- s = tmp - q * s;
- }
-
- *x = a >= 0 ? old_s : -old_s;
- if (b != 0) {
- *y = (old_r - (*x) * a) / b;
- } else {
- *y = 1;
- }
-
- return old_r;
-}
-
/*!
* \brief Take GCD of a and b.
* \param a The first operand.
* \param b The second operand.
* \return The result.
*/
-inline int64_t ZeroAwareGCD(int64_t a, int64_t b) {
- if (a < 0) a = -a;
- if (b < 0) b = -b;
- if (a < b) std::swap(a, b);
- if (b == 0) return a;
- // perform GCD (greatest common divisor)
- // ax + by = gcd(a, b) z if a != 0, b != 0
- while (a % b != 0) {
- a = a % b;
- std::swap(a, b);
- }
- return b;
-}
-
inline ffi::BigInt ZeroAwareGCD(ffi::BigInt a, ffi::BigInt b) {
auto a_int64 = a.as<int64_t>();
auto b_int64 = b.as<int64_t>();
- // The native overload cannot negate INT64_MIN.
+ // std::gcd requires both absolute values to be representable as int64_t.
if (a_int64.has_value() && b_int64.has_value() &&
*a_int64 != std::numeric_limits<int64_t>::min() &&
*b_int64 != std::numeric_limits<int64_t>::min()) {
- return ZeroAwareGCD(*a_int64, *b_int64);
+ return std::gcd(*a_int64, *b_int64);
}
if (a < 0) a = -a;
if (b < 0) b = -b;
@@ -177,17 +120,6 @@ inline ffi::BigInt ZeroAwareGCD(ffi::BigInt a, ffi::BigInt
b) {
return b;
}
-/*!
- * \brief Calculate the least common multiple for two values.
- * \param a an integer number
- * \param b an integer number
- * \return the least common multiple.
- */
-inline int64_t LeastCommonMultiple(int64_t a, int64_t b) {
- int64_t x, y;
- return (a * b) / ExtendedEuclidean(a, b, &x, &y);
-}
-
} // namespace sym
} // namespace tvm
#endif // TVM_SYM_INT_OPERATOR_H_
diff --git a/src/sym/int_set.cc b/src/sym/int_set.cc
index beb21d9f58..2c9e3cb8ad 100644
--- a/src/sym/int_set.cc
+++ b/src/sym/int_set.cc
@@ -1012,6 +1012,10 @@ ffi::Array<IntSet> UnionRegionLowerBound(const
ffi::Array<ffi::Array<IntSet>>& n
return result;
}
+IntSet Intersect(const IntSet& lhs, const IntSet& rhs, const Analyzer&
analyzer) {
+ return Intersect(analyzer.get(), ToIntervalSet(lhs), ToIntervalSet(rhs));
+}
+
IntSet Intersect(const ffi::Array<IntSet>& sets) {
if (sets.size() == 0) return IntSet::Nothing();
if (sets.size() == 1) return sets[0];
diff --git a/src/sym/modular_set.cc b/src/sym/modular_set.cc
index dbbca70018..fd63276a39 100644
--- a/src/sym/modular_set.cc
+++ b/src/sym/modular_set.cc
@@ -29,6 +29,7 @@
#include <tvm/sym/analyzer.h>
#include <limits>
+#include <numeric>
#include <unordered_map>
#include <utility>
@@ -38,6 +39,44 @@ namespace tvm {
namespace sym {
using namespace tvm::prim;
+namespace {
+int64_t ExtendedEuclidean(int64_t a, int64_t b, int64_t* x, int64_t* y) {
+ // Extended Euclidean algorithm
+ // if a < 0, the problem can be convert into
+ // |a|* (-x) + b * y = gcd(|a|, b)
+ //
+ // initial condition:
+ // a * 0 + b * 1 = b
+ // a * 1 + b * 0 = a
+ int64_t s = 0, old_s = 1;
+ int64_t r = b, old_r = a >= 0 ? a : -a;
+ // Iteration (r2 < r1):
+ // a * x1 + b * y1 = r1
+ // a * x2 + b * y2 = r2
+ // The above two eqs can derive the following eq (q = r1 / r2)
+ // a * (x1 - x2 * q) + b * (y1 - y2 * q) = r1 - r2 * q = r3
+ // Because r3 < r2, the iteration can eventually terminate
+ while (r != 0) {
+ int64_t q = old_r / r;
+ int64_t tmp = old_r;
+ old_r = r;
+ r = tmp - q * r;
+ tmp = old_s;
+ old_s = s;
+ s = tmp - q * s;
+ }
+
+ *x = a >= 0 ? old_s : -old_s;
+ if (b != 0) {
+ *y = (old_r - (*x) * a) / b;
+ } else {
+ *y = 1;
+ }
+
+ return old_r;
+}
+} // namespace
+
TVM_FFI_STATIC_INIT_BLOCK() { ModularSetObj::RegisterReflection(); }
ModularSet::ModularSet(int64_t coeff, int64_t base) {
@@ -158,14 +197,14 @@ class ModularSetAnalyzer::Impl : public
tvm::ExprFunctor<ModularSetAnalyzer::Ent
Entry Dispatch_(const prim::AddNode* op) final {
Entry a = Dispatch(op->a);
Entry b = Dispatch(op->b);
- int64_t coeff = ZeroAwareGCD(a.coeff, b.coeff);
+ int64_t coeff = std::gcd(a.coeff, b.coeff);
return Entry(coeff, a.base + b.base);
}
Entry Dispatch_(const prim::SubNode* op) final {
Entry a = Dispatch(op->a);
Entry b = Dispatch(op->b);
- int64_t coeff = ZeroAwareGCD(a.coeff, b.coeff);
+ int64_t coeff = std::gcd(a.coeff, b.coeff);
return Entry(coeff, a.base - b.base);
}
@@ -179,7 +218,7 @@ class ModularSetAnalyzer::Impl : public
tvm::ExprFunctor<ModularSetAnalyzer::Ent
int64_t pq = a.coeff * b.coeff;
int64_t pm = a.coeff * b.base;
int64_t qn = a.base * b.coeff;
- int64_t coeff = ZeroAwareGCD(pq, ZeroAwareGCD(pm, qn));
+ int64_t coeff = std::gcd(pq, std::gcd(pm, qn));
return Entry(coeff, a.base * b.base);
}
@@ -237,7 +276,7 @@ class ModularSetAnalyzer::Impl : public
tvm::ExprFunctor<ModularSetAnalyzer::Ent
Entry ModByConst(const PrimExpr& lhs, int64_t val, bool round_down) {
Entry a = Dispatch(lhs);
TVM_FFI_ICHECK_NE(val, 0);
- int64_t coeff = ZeroAwareGCD(a.coeff, val);
+ int64_t coeff = std::gcd(a.coeff, val);
if (a.base % coeff == 0 ||
(a.base > 0 && (round_down || parent_->CanProveGreaterEqual(lhs, 0))))
{
return Entry(coeff, a.base % coeff);
@@ -330,7 +369,7 @@ class ModularSetAnalyzer::Impl : public
tvm::ExprFunctor<ModularSetAnalyzer::Ent
*/
static Entry Union(Entry a, Entry b) {
// {ax + y} \cup {bz + h} => {gcd(a, b) x + {y or h}}
- int64_t coeff = ZeroAwareGCD(a.coeff, b.coeff);
+ int64_t coeff = std::gcd(a.coeff, b.coeff);
if (coeff == 0) {
if (a.base == b.base) return a;
return Everything();
@@ -340,7 +379,7 @@ class ModularSetAnalyzer::Impl : public
tvm::ExprFunctor<ModularSetAnalyzer::Ent
if (base0 == base1) {
return Entry(coeff, base0);
} else {
- return Entry(ZeroAwareGCD(ZeroAwareGCD(base0, base1), coeff), base0);
+ return Entry(std::gcd(std::gcd(base0, base1), coeff), base0);
}
}
diff --git a/src/sym/presburger_set.cc b/src/sym/presburger_set.cc
deleted file mode 100644
index 2d19acf37b..0000000000
--- a/src/sym/presburger_set.cc
+++ /dev/null
@@ -1,293 +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 presburger_set.cc
- * \brief The presburger set functions
- */
-#include "presburger_set.h"
-
-#include <tvm/ffi/cast.h>
-#include <tvm/ffi/extra/structural_visit.h>
-#include <tvm/ffi/function.h>
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/ir/prim/expr.h>
-#include <tvm/sym/int_set.h>
-#include <tvm/sym/pattern.h>
-
-#include <algorithm>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "constraint_extract.h"
-#include "interval_set.h"
-
-namespace tvm {
-namespace sym {
-using namespace tvm::prim;
-
-#if defined(TVM_MLIR_VERSION) && TVM_MLIR_VERSION >= 150
-
-TVM_FFI_STATIC_INIT_BLOCK() { PresburgerSetObj::RegisterReflection(); }
-
-static int64_t GetPresburgerCoefficient(const PrimExpr& coefficient) {
- const auto* imm = coefficient.as<IntImmNode>();
- auto value = imm ? imm->value.as<int64_t>() : std::nullopt;
- TVM_FFI_ICHECK(value.has_value())
- << "Presburger coefficients must be constant integers that fit int64: "
<< coefficient;
- return *value;
-}
-
-static void Update(const PrimExpr& constraint, PresburgerSetObj* intset) {
- auto& space = intset->space;
- auto constraints_union = ExtractComponents(constraint);
- for (const PrimExpr& subconstraint : constraints_union) {
- auto entries = ExtractConstraints(subconstraint, false);
- auto vars = intset->GetVars();
- IntegerRelation disjunct(entries.size(), 0, vars.size() + 1, space);
- for (const PrimExpr& entry : entries) {
- // The expression is expect to be simplified to only contain ==, <= or <
- if (entry.as<prim::LENode>()) {
- auto coeffs_a = DetectLinearEquation(entry.as<prim::LENode>()->a,
vars);
- auto coeffs_b = DetectLinearEquation(entry.as<prim::LENode>()->b,
vars);
- std::vector<int64_t> int_coeffs;
- for (size_t i = 0; i < coeffs_a.size(); i++) {
- int_coeffs.push_back(GetPresburgerCoefficient(coeffs_b[i]) -
- GetPresburgerCoefficient(coeffs_a[i]));
- }
- disjunct.addInequality(int_coeffs);
- } else if (entry.as<prim::LTNode>()) {
- auto coeffs_a = DetectLinearEquation(entry.as<prim::LTNode>()->a,
vars);
- auto coeffs_b = DetectLinearEquation(entry.as<prim::LTNode>()->b,
vars);
- std::vector<int64_t> int_coeffs;
- for (size_t i = 0; i < coeffs_a.size(); i++) {
- int_coeffs.push_back(GetPresburgerCoefficient(coeffs_b[i]) -
- GetPresburgerCoefficient(coeffs_a[i]));
- }
- int_coeffs[int_coeffs.size() - 1] -= 1;
- disjunct.addInequality(int_coeffs);
- } else if (entry.as<prim::EQNode>()) {
- auto coeffs_a = DetectLinearEquation(entry.as<prim::EQNode>()->a,
vars);
- auto coeffs_b = DetectLinearEquation(entry.as<prim::EQNode>()->b,
vars);
- std::vector<int64_t> int_coeffs;
- for (size_t i = 0; i < coeffs_a.size(); i++) {
- int_coeffs.push_back(GetPresburgerCoefficient(coeffs_a[i]) -
- GetPresburgerCoefficient(coeffs_b[i]));
- }
- disjunct.addEquality(int_coeffs);
- } else {
- TVM_FFI_THROW(InternalError)
- << "Unsupported constraint expression: " << entry->GetTypeKey();
- }
- }
- intset->unionInPlace(disjunct);
- }
-}
-
-PresburgerSet::PresburgerSet(const PrimExpr& constraint) :
IntSet(ffi::UnsafeInit{}) {
- ffi::Array<PrimVar> vars;
- ffi::StructuralWalk<ffi::WalkOrder::kPostOrder>(
- constraint, [&vars](const Var& var) -> ffi::Expected<ffi::WalkResult> {
- if (auto prim_var = var.as<PrimVar>()) {
- PrimVar var = *prim_var;
- if (!std::any_of(vars.begin(), vars.end(),
- [&var](const PrimVar& v) { return v.same_as(var);
})) {
- vars.push_back(var);
- }
- }
- return ffi::WalkResult::Advance();
- });
- auto constraints_union = ExtractComponents(constraint);
- Analyzer analyzer;
- PrimExpr simplified_constraint = analyzer->Simplify(constraint,
kSimplifyRewriteCanonicalRewrite);
- auto space = PresburgerSpace::getRelationSpace(vars.size(), 0, 0, 0);
- auto node = ffi::make_object<PresburgerSetObj>(std::move(space), vars);
- node->SetVars(vars);
- Update(simplified_constraint, node.get());
- data_ = std::move(node);
-}
-
-PresburgerSet::PresburgerSet(const std::vector<IntegerRelation>& disjuncts,
- const ffi::Array<PrimVar>& vars)
- : IntSet(ffi::UnsafeInit{}) {
- auto node = ffi::make_object<PresburgerSetObj>(disjuncts,
disjuncts[0].getSpace(), vars);
- data_ = std::move(node);
-}
-
-void PresburgerSetObj::UpdateConstraint(const PrimExpr& constraint,
- const ffi::Array<PrimVar>& vars) {
- Analyzer analyzer;
- PrimExpr simplified_constraint = analyzer->Simplify(constraint,
kSimplifyRewriteCanonicalRewrite);
- Update(simplified_constraint, this);
- SetVars(vars);
-}
-
-PrimExpr PresburgerSetObj::GenerateConstraint() const {
- PrimExpr constraint = IntImm::Bool(false);
- for (const IntegerRelation& disjunct : disjuncts) {
- PrimExpr union_entry = IntImm::Bool(true);
- for (unsigned i = 0, e = disjunct.getNumEqualities(); i < e; ++i) {
- PrimExpr linear_eq = IntImm::Int64(0);
- if (disjunct.getNumCols() > 1) {
- for (unsigned j = 0, f = disjunct.getNumCols() - 1; j < f; ++j) {
-#if TVM_MLIR_VERSION >= 160
- auto coeff = int64_t(disjunct.atEq(i, j));
-#else
- auto coeff = disjunct.atEq(i, j);
-#endif
- if (coeff >= 0 || IsZero(linear_eq)) {
- linear_eq = linear_eq + IntImm::Int64(coeff) * vars[j];
- } else {
- linear_eq = linear_eq - IntImm::Int64(-coeff) * vars[j];
- }
- }
- }
-#if TVM_MLIR_VERSION >= 160
- auto c0 = int64_t(disjunct.atEq(i, disjunct.getNumCols() - 1));
-#else
- auto c0 = disjunct.atEq(i, disjunct.getNumCols() - 1);
-#endif
- linear_eq = linear_eq + IntImm::Int64(c0);
- union_entry = (union_entry && (linear_eq == 0));
- }
- for (unsigned i = 0, e = disjunct.getNumInequalities(); i < e; ++i) {
- PrimExpr linear_eq = IntImm::Int64(0);
- if (disjunct.getNumCols() > 1) {
- for (unsigned j = 0, f = disjunct.getNumCols() - 1; j < f; ++j) {
-#if TVM_MLIR_VERSION >= 160
- auto coeff = int64_t(disjunct.atIneq(i, j));
-#else
- auto coeff = disjunct.atIneq(i, j);
-#endif
- if (coeff >= 0 || IsZero(linear_eq)) {
- linear_eq = linear_eq + IntImm::Int64(coeff) * vars[j];
- } else {
- linear_eq = linear_eq - IntImm::Int64(-coeff) * vars[j];
- }
- }
- }
-#if TVM_MLIR_VERSION >= 160
- auto c0 = int64_t(disjunct.atIneq(i, disjunct.getNumCols() - 1));
-#else
- auto c0 = disjunct.atIneq(i, disjunct.getNumCols() - 1);
-#endif
- if (c0 >= 0) {
- linear_eq = linear_eq + IntImm::Int64(c0);
- } else {
- linear_eq = linear_eq - IntImm::Int64(-c0);
- }
- union_entry = (union_entry && (linear_eq >= 0));
- }
- constraint = constraint || union_entry;
- }
-
- return constraint;
-}
-
-PresburgerSet Union(const ffi::Array<PresburgerSet>& sets) {
- TVM_FFI_ICHECK_GT(sets.size(), 0);
- if (sets.size() == 1) return sets[0];
- auto relations = sets[0]->disjuncts;
- for (size_t i = 1; i < sets.size(); ++i) {
- for (const IntegerRelation& rel : sets[i]->disjuncts) {
- relations.push_back(rel);
- }
- }
- return PresburgerSet(std::move(relations), sets[0]->GetVars());
-}
-
-PresburgerSet Intersect(const ffi::Array<PresburgerSet>& sets) {
- TVM_FFI_ICHECK_GT(sets.size(), 0);
- if (sets.size() == 1) return sets[0];
- auto relations = sets[0]->disjuncts;
- const auto& space = sets[0]->space;
-
- for (size_t i = 1; i < sets.size(); ++i) {
- TVM_FFI_ICHECK(space.isCompatible(sets[i]->space)) << "Spaces should
match";
- for (const IntegerRelation& relA : sets[i]->disjuncts) {
- for (const IntegerRelation& relB : relations) {
- IntegerRelation intersection = relA.intersect(relB);
- if (!intersection.isEmpty()) relations.push_back(intersection);
- }
- }
- }
- return PresburgerSet(std::move(relations), sets[0]->GetVars());
-}
-
-IntSet EvalSet(const PrimExpr& e, const PresburgerSet& set) {
- ffi::Array<PrimExpr> tvm_coeffs = DetectLinearEquation(e, set->GetVars());
-#if TVM_MLIR_VERSION >= 190
- SmallVector<llvm::DynamicAPInt> coeffs;
-#elif TVM_MLIR_VERSION >= 160
- SmallVector<mlir::presburger::MPInt> coeffs;
-#else
- SmallVector<int64_t> coeffs;
-#endif
-
- coeffs.reserve(tvm_coeffs.size());
- for (const PrimExpr& it : tvm_coeffs) {
-#if TVM_MLIR_VERSION >= 190
- coeffs.push_back(llvm::DynamicAPInt(GetPresburgerCoefficient(it)));
-#elif TVM_MLIR_VERSION >= 160
- coeffs.push_back(mlir::presburger::MPInt(GetPresburgerCoefficient(it)));
-#else
- coeffs.push_back(GetPresburgerCoefficient(it));
-#endif
- }
-
- IntSet result = IntSet::Nothing();
- for (const IntegerRelation& it : set->disjuncts) {
- Simplex simplex(it);
- auto range = simplex.computeIntegerBounds(coeffs);
- auto maxRoundedDown(simplex.computeOptimum(Simplex::Direction::Up,
coeffs));
- auto opt = range.first.getOptimumIfBounded();
-#if TVM_MLIR_VERSION >= 160
- auto min = opt.has_value() ? IntImm::Int64(int64_t(opt.value())) :
neg_inf();
-#else
- auto min = opt.hasValue() ? IntImm::Int64(opt.getValue()) : neg_inf();
-#endif
- opt = range.second.getOptimumIfBounded();
-#if TVM_MLIR_VERSION >= 160
- auto max = opt.has_value() ? IntImm::Int64(int64_t(opt.value())) :
pos_inf();
-#else
- auto max = opt.hasValue() ? IntImm::Int64(opt.getValue()) : pos_inf();
-#endif
- auto interval = IntervalSet(min, max);
- result = Union({result, interval});
- }
- return result;
-}
-
-// Pattern A (RM): auto-default repr from reflection.
-
-#else // defined(TVM_MLIR_VERSION) && TVM_MLIR_VERSION >= 150
-
-PresburgerSet MakePresburgerSet(const PrimExpr& constraint) { return
PresburgerSet(constraint); }
-
-TVM_FFI_STATIC_INIT_BLOCK() {
- namespace refl = tvm::ffi::reflection;
- PresburgerSetObj::RegisterReflection();
- refl::GlobalDef().def("sym.PresburgerSet", MakePresburgerSet);
-}
-
-#endif // defined(TVM_MLIR_VERSION) && TVM_MLIR_VERSION >= 150
-
-} // namespace sym
-} // namespace tvm
diff --git a/src/sym/presburger_set.h b/src/sym/presburger_set.h
deleted file mode 100644
index 85074f211a..0000000000
--- a/src/sym/presburger_set.h
+++ /dev/null
@@ -1,201 +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 presburger_set.h
- * \brief Integer set based on MLIR Presburger set
- */
-#ifndef TVM_SYM_PRESBURGER_SET_H_
-#define TVM_SYM_PRESBURGER_SET_H_
-
-#ifdef TVM_MLIR_VERSION
-#if TVM_MLIR_VERSION >= 150
-#include <mlir/Analysis/Presburger/IntegerRelation.h>
-#include <mlir/Analysis/Presburger/PresburgerRelation.h>
-#include <mlir/Analysis/Presburger/Simplex.h>
-#endif
-#endif
-
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/ir/cow.h>
-#include <tvm/ir/prim/op.h>
-#include <tvm/sym/analyzer.h>
-
-#include <limits>
-#include <vector>
-
-#include "const_fold.h"
-
-namespace tvm {
-namespace sym {
-
-#ifdef TVM_MLIR_VERSION
-#if TVM_MLIR_VERSION >= 150
-using namespace mlir;
-using namespace presburger;
-
-// Acknowledgement: PresburgerSet is based on Presburger set of MLIR.
-/*!
- * \brief Symbolic integer set.
- *
- * \note PresburgerSet aims to provide compatible APIs with IntSet,
- * and some additional APIs that analyze and solve
- * multi-dimension interger set problems
- */
-class PresburgerSetObj : public IntSetObj {
- public:
- PresburgerSetObj() : space(PresburgerSpace::getRelationSpace()) {}
- explicit PresburgerSetObj(const PresburgerSpace& space, const
ffi::Array<PrimVar>& vars)
- : disjuncts({}), space(space), vars(vars) {}
- explicit PresburgerSetObj(const std::vector<IntegerRelation>& disjuncts,
- const PresburgerSpace& space, const
ffi::Array<PrimVar>& vars)
- : disjuncts(disjuncts), space(space), vars(vars) {}
-
- /*! \brief Represent the union of multiple IntegerRelation */
- std::vector<IntegerRelation> disjuncts;
- /*! \brief The space of all the disjuncts */
- PresburgerSpace space;
-
- // visitor overload.
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<PresburgerSetObj>();
- }
-
- /*!
- * \brief Do inplace union with given disjunct
- * \param disjunct The given disjunct to be union with
- */
- void unionInPlace(const IntegerRelation& disjunct) {
- assert(space.isCompatible(disjunct.getSpace()) && "Spaces should match");
- disjuncts.push_back(disjunct);
- }
-
- /*!
- * \brief Update int set with given constraint
- * \param constraint The added constraint to the PresburgerSet.
- * \param vars The specified domain vars in constraint expression.
- */
- void UpdateConstraint(const PrimExpr& constraint, const ffi::Array<PrimVar>&
vars);
-
- /*!
- * \brief Generate expression that represents the constraint
- * \return The generated expression
- */
- PrimExpr GenerateConstraint() const;
-
- /*!
- * \brief Set domain vars
- * \param new_vars Vars that will be taken as the domain vars
- */
- void SetVars(const ffi::Array<PrimVar>& new_vars) { vars = new_vars; }
-
- /*!
- * \brief Get the current domain vars
- * \return The current doamin vars
- */
- ffi::Array<PrimVar> GetVars() const { return vars; }
-
- /*! \return whether integer set is empty */
- bool IsEmpty() const {
- return std::all_of(disjuncts.begin(), disjuncts.end(),
- std::mem_fn(&IntegerRelation::isIntegerEmpty));
- }
- TVM_FFI_DECLARE_OBJECT_INFO_FINAL("sym.PresburgerSet", PresburgerSetObj,
IntSetObj);
-
- private:
- ffi::Array<PrimVar> vars;
-};
-
-/*!
- * \brief Integer set used for multi-dimension integer analysis.
- * \sa PresburgerSetObj
- */
-class PresburgerSet : public IntSet {
- public:
- /*!
- * \brief Make a new instance of PresburgerSet.
- * \param disjuncts The disjunts to construct the set.
- * \param vars The variables that the constraint describes about.
- * \return The created PresburgerSet.
- */
- TVM_TIRX_DLL PresburgerSet(const std::vector<IntegerRelation>& disjuncts,
- const ffi::Array<PrimVar>& vars);
-
- /*!
- * \brief Make a new instance of PresburgerSet, collect all vars as space
vars.
- * \param constraint The constraint to construct the set.
- * \return The created PresburgerSet.
- */
- TVM_TIRX_DLL PresburgerSet(const PrimExpr& constraint);
-
- TVM_DEFINE_OBJECT_REF_COW_METHOD(PresburgerSetObj);
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PresburgerSet, IntSet,
PresburgerSetObj);
-};
-#endif // TVM_MLIR_VERSION >= 150
-#else // TVM_MLIR_VERSION
-// Class definition when MLIR is not enabled, to prevent compile-time error.
-class PresburgerSetObj : public IntSetObj {
- public:
- // dummy visitor overload.
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<PresburgerSetObj>();
- }
- TVM_FFI_DECLARE_OBJECT_INFO_FINAL("sym.PresburgerSet", PresburgerSetObj,
IntSetObj);
-};
-
-class PresburgerSet : public IntSet {
- public:
- /*!
- * \brief Constructor interface to prompt when MLIR is not enabled.
- * \param constraint The constraint to construct the set.
- * \return The created set.
- */
- TVM_TIRX_DLL PresburgerSet(const PrimExpr& constraint) :
IntSet(ffi::UnsafeInit{}) {
- TVM_FFI_THROW(InternalError) << "MLIR is not enabled!";
- }
-};
-#endif // TVM_MLIR_VERSION
-/*!
- * \brief Create a union set of all sets
- * \param sets The sets to be combined
- * \return the set after union
- */
-PresburgerSet Union(const ffi::Array<PresburgerSet>& sets);
-
-/*!
- * \brief Create an intersected set of all sets
- * \param sets The sets to be intersected
- * \return The intersect set
- */
-PresburgerSet Intersect(const ffi::Array<PresburgerSet>& sets);
-
-/*!
- * \brief Evaluate the range of given expression based on the constraint
- * in given PresburgerSet
- * \param e The target expresision to be evaluated.
- * \param set The PresburgerSet defining the constraint.
- */
-IntSet EvalSet(const PrimExpr& e, const PresburgerSet& set);
-
-} // namespace sym
-} // namespace tvm
-
-#endif // TVM_SYM_PRESBURGER_SET_H_
diff --git a/src/sym/rewrite_simplify.cc b/src/sym/rewrite_simplify.cc
index fc75e5f95f..be3b607fa2 100644
--- a/src/sym/rewrite_simplify.cc
+++ b/src/sym/rewrite_simplify.cc
@@ -32,6 +32,7 @@
#include <tvm/sym/analyzer.h>
#include <algorithm>
+#include <numeric>
#include <tuple>
#include <utility>
@@ -2104,9 +2105,9 @@ PrimExpr
RewriteSimplifier::Impl::ApplyRewriteRules(prim::LT ret, InplaceMode in
auto common_factor = [&]() -> int64_t {
auto modular_a = analyzer_->modular_set(ret->a);
auto modular_b = analyzer_->modular_set(ret->b);
- auto gcd_lhs = ZeroAwareGCD(modular_a->base, modular_a->coeff);
- auto gcd_rhs = ZeroAwareGCD(modular_b->base, modular_b->coeff);
- return ZeroAwareGCD(gcd_lhs, gcd_rhs);
+ auto gcd_lhs = std::gcd(modular_a->base, modular_a->coeff);
+ auto gcd_rhs = std::gcd(modular_b->base, modular_b->coeff);
+ return std::gcd(gcd_lhs, gcd_rhs);
}();
if (common_factor > 1) {
return RecursiveRewrite(floordiv(ret->a, common_factor) <
floordiv(ret->b, common_factor),
diff --git
a/tests/python/relax/test_eliminate_pad_branch_using_buffer_assumption.py
b/tests/python/relax/test_eliminate_pad_branch_using_buffer_assumption.py
deleted file mode 100644
index 69ce2d8ef6..0000000000
--- a/tests/python/relax/test_eliminate_pad_branch_using_buffer_assumption.py
+++ /dev/null
@@ -1,646 +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.
-# pylint: disable=missing-docstring, unused-variable
-# ruff: noqa: F841
-
-# The test attempts to eliminate redundant pad branch and overcompute the
value for elementwise ops.
-# This helps to expose more opportunities to vectorize the code.
-
-import tvm_ffi
-
-import tvm
-import tvm.script
-import tvm.testing
-from tvm.script import relax as R
-from tvm.script import s_tir as Ts
-from tvm.script import tirx as T
-
-
[email protected]_module
-class AddBefore:
- @Ts.function(private=True)
- def add(
- a: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- b: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- compute: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- ):
- T.func_attr(
- {
- "op_attrs": {"lhs_axis": 0, "op_name": "qnn.add", "rhs_axis":
0},
- "op_pattern": 0,
- "operator_name": "add",
- "tirx.noalias": True,
- }
- )
- # with Ts.sblock("root"):
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_A_assumptions"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- not (
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5)
- )
- or a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_B_assumptions"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- not (
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5)
- )
- or b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("compute"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(
- a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6],
- b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6],
- )
- Ts.writes(compute[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- compute[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6] = (
- T.if_then_else(
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5),
- T.uint8(0),
- a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- + b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- )
- )
-
- @R.function
- def main(
- a: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- b: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- ) -> R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"):
- out = R.call_tir(
- AddBefore.add,
- (a, b),
- ty_args=[R.Tensor((1, 4, 4, 16, 8, 8, 32), dtype="uint8")],
- )
- return out
-
-
[email protected]_module
-class AddExpected:
- @Ts.function(private=True)
- def add(
- a: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- b: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- compute: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- ):
- T.func_attr(
- {
- "op_attrs": {"lhs_axis": 0, "op_name": "qnn.add", "rhs_axis":
0},
- "op_pattern": 0,
- "operator_name": "add",
- "tirx.noalias": True,
- }
- )
- # with Ts.sblock("root"):
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_A_assumptions"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6 =
Ts.axis.remap(
- "SSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6]
- )
- Ts.reads(a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- (
- (v_axis1 < T.int64(3) or v_axis4 < T.int64(4))
- and (v_axis2 < T.int64(3) or v_axis5 < T.int64(4))
- )
- or a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_B_assumptions"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6 =
Ts.axis.remap(
- "SSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6]
- )
- Ts.reads(b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- (
- (v_axis1 < T.int64(3) or v_axis4 < T.int64(4))
- and (v_axis2 < T.int64(3) or v_axis5 < T.int64(4))
- )
- or b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5_0 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(2)
- ):
- for axis5_1_axis6_fused in T.vectorized(T.int64(128)):
- with Ts.sblock("compute"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4 = Ts.axis.remap(
- "SSSS", [axis1, axis2, axis3, axis4]
- )
- v_axis5 = Ts.axis.spatial(
- T.int64(8), axis5_0 * T.int64(4) + axis5_1_axis6_fused
// T.int64(32)
- )
- v_axis6 = Ts.axis.spatial(T.int64(32), axis5_1_axis6_fused
% T.int64(32))
- Ts.reads(
- a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- )
- Ts.writes(
- compute[T.int64(0), v_axis1, v_axis2, v_axis3,
v_axis4, v_axis5, v_axis6]
- )
- compute[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6] = (
- a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- + b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- )
-
- @R.function
- def main(
- a: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- b: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- ) -> R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"):
- out = R.call_tir(
- AddExpected.add,
- (a, b),
- ty_args=[R.Tensor((1, 4, 4, 16, 8, 8, 32), dtype="uint8")],
- )
- return out
-
-
[email protected]_module
-class SubBefore:
- @Ts.function(private=True)
- def sub(
- a: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- b: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- compute: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- ):
- T.func_attr(
- {
- "op_attrs": {"lhs_axis": 0, "op_name": "qnn.subtract",
"rhs_axis": 0},
- "op_pattern": 0,
- "operator_name": "sub",
- "tirx.noalias": True,
- }
- )
- # with Ts.sblock("root"):
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_A_assumptions"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- not (
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5)
- )
- or a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_B_assumptions"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- not (
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5)
- )
- or b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("compute"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(
- a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6],
- b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6],
- )
- Ts.writes(compute[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- compute[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6] = (
- T.if_then_else(
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5),
- T.uint8(0),
- a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- - b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- )
- )
-
- @R.function
- def main(
- a: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- b: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- ) -> R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"):
- out = R.call_tir(
- SubBefore.sub,
- (a, b),
- ty_args=[R.Tensor((1, 4, 4, 16, 8, 8, 32), dtype="uint8")],
- )
- return out
-
-
[email protected]_module
-class SubExpected:
- @Ts.function(private=True)
- def sub(
- a: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- b: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- compute: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- ):
- T.func_attr(
- {
- "op_attrs": {"lhs_axis": 0, "op_name": "qnn.subtract",
"rhs_axis": 0},
- "op_pattern": 0,
- "operator_name": "sub",
- "tirx.noalias": True,
- }
- )
- # with Ts.sblock("root"):
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_A_assumptions"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6 =
Ts.axis.remap(
- "SSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6]
- )
- Ts.reads(a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- (
- (v_axis1 < T.int64(3) or v_axis4 < T.int64(4))
- and (v_axis2 < T.int64(3) or v_axis5 < T.int64(4))
- )
- or a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_B_assumptions"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6 =
Ts.axis.remap(
- "SSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6]
- )
- Ts.reads(b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- (
- (v_axis1 < T.int64(3) or v_axis4 < T.int64(4))
- and (v_axis2 < T.int64(3) or v_axis5 < T.int64(4))
- )
- or b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5_0 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(2)
- ):
- for axis5_1_axis6_fused in T.vectorized(T.int64(128)):
- with Ts.sblock("compute"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4 = Ts.axis.remap(
- "SSSS", [axis1, axis2, axis3, axis4]
- )
- v_axis5 = Ts.axis.spatial(
- T.int64(8), axis5_0 * T.int64(4) + axis5_1_axis6_fused
// T.int64(32)
- )
- v_axis6 = Ts.axis.spatial(T.int64(32), axis5_1_axis6_fused
% T.int64(32))
- Ts.reads(
- a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- )
- Ts.writes(
- compute[T.int64(0), v_axis1, v_axis2, v_axis3,
v_axis4, v_axis5, v_axis6]
- )
- compute[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6] = (
- a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- - b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- )
-
- @R.function
- def main(
- a: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- b: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- ) -> R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"):
- out = R.call_tir(
- SubExpected.sub,
- (a, b),
- ty_args=[R.Tensor((1, 4, 4, 16, 8, 8, 32), dtype="uint8")],
- )
- return out
-
-
[email protected]_module
-class MulBefore:
- @Ts.function(private=True)
- def mul(
- a: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- b: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- compute: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- ):
- T.func_attr(
- {
- "op_attrs": {"lhs_axis": 0, "op_name": "qnn.mul", "rhs_axis":
0},
- "op_pattern": 0,
- "operator_name": "mul",
- "tirx.noalias": True,
- }
- )
- # with Ts.sblock("root"):
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_A_assumptions"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- not (
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5)
- )
- or a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_B_assumptions"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- not (
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5)
- )
- or b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("compute"):
- v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6
= Ts.axis.remap(
- "SSSSSSS", [axis0, axis1, axis2, axis3, axis4, axis5,
axis6]
- )
- Ts.reads(
- a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6],
- b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6],
- )
- Ts.writes(compute[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- compute[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6] = (
- T.if_then_else(
- (v_axis1 == T.int64(3) and T.int64(4) <= v_axis4)
- or (v_axis2 == T.int64(3) and T.int64(4) <= v_axis5),
- T.uint8(0),
- a[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- * b[v_axis0, v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- )
- )
-
- @R.function
- def main(
- a: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- b: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- ) -> R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"):
- out = R.call_tir(
- MulBefore.mul,
- (a, b),
- ty_args=[R.Tensor((1, 4, 4, 16, 8, 8, 32), dtype="uint8")],
- )
- return out
-
-
[email protected]_module
-class MulExpected:
- @Ts.function(private=True)
- def mul(
- a: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- b: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- compute: T.Tensor(
- (T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)),
- "uint8",
- ),
- ):
- T.func_attr(
- {
- "op_attrs": {"lhs_axis": 0, "op_name": "qnn.mul", "rhs_axis":
0},
- "op_pattern": 0,
- "operator_name": "mul",
- "tirx.noalias": True,
- }
- )
- # with Ts.sblock("root"):
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_A_assumptions"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6 =
Ts.axis.remap(
- "SSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6]
- )
- Ts.reads(a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- (
- (v_axis1 < T.int64(3) or v_axis4 < T.int64(4))
- and (v_axis2 < T.int64(3) or v_axis5 < T.int64(4))
- )
- or a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5, axis6 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(8), T.int64(32)
- ):
- with Ts.sblock("buffer_B_assumptions"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6 =
Ts.axis.remap(
- "SSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6]
- )
- Ts.reads(b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6])
- Ts.writes()
- T.assume(
- (
- (v_axis1 < T.int64(3) or v_axis4 < T.int64(4))
- and (v_axis2 < T.int64(3) or v_axis5 < T.int64(4))
- )
- or b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- == T.uint8(0)
- )
-
- for axis0, axis1, axis2, axis3, axis4, axis5_0 in T.grid(
- T.int64(1), T.int64(4), T.int64(4), T.int64(16), T.int64(8),
T.int64(2)
- ):
- for axis5_1_axis6_fused in T.vectorized(T.int64(128)):
- with Ts.sblock("compute"):
- v_axis0 = Ts.axis.spatial(T.int64(1), T.int64(0))
- v_axis1, v_axis2, v_axis3, v_axis4 = Ts.axis.remap(
- "SSSS", [axis1, axis2, axis3, axis4]
- )
- v_axis5 = Ts.axis.spatial(
- T.int64(8), axis5_0 * T.int64(4) + axis5_1_axis6_fused
// T.int64(32)
- )
- v_axis6 = Ts.axis.spatial(T.int64(32), axis5_1_axis6_fused
% T.int64(32))
- Ts.reads(
- a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6],
- )
- Ts.writes(
- compute[T.int64(0), v_axis1, v_axis2, v_axis3,
v_axis4, v_axis5, v_axis6]
- )
- compute[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6] = (
- a[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- * b[T.int64(0), v_axis1, v_axis2, v_axis3, v_axis4,
v_axis5, v_axis6]
- )
-
- @R.function
- def main(
- a: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- b: R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"),
- ) -> R.Tensor((1, 4, 4, 16, 8, 8, 32), "uint8"):
- out = R.call_tir(
- MulExpected.mul,
- (a, b),
- ty_args=[R.Tensor((1, 4, 4, 16, 8, 8, 32), dtype="uint8")],
- )
- return out
-
-
-def test_add_function_overcompute():
- add_after = tvm.s_tir.transform.UseAssumeToReduceBranches()(AddBefore)
- tvm_ffi.structural_equal(add_after["add"], AddExpected["add"],
map_free_vars=True)
-
-
-def test_sub_function_overcompute():
- sub_after = tvm.s_tir.transform.UseAssumeToReduceBranches()(SubBefore)
- tvm_ffi.structural_equal(sub_after["sub"], SubExpected["sub"],
map_free_vars=True)
-
-
-def test_mul_function_overcompute():
- mul_after = tvm.s_tir.transform.UseAssumeToReduceBranches()(MulBefore)
- tvm_ffi.structural_equal(mul_after["mul"], MulExpected["mul"],
map_free_vars=True)
-
-
-if __name__ == "__main__":
- tvm.testing.main()