This is an automated email from the ASF dual-hosted git repository.
sanirudh 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 bd24133fce [TIR][Compute-at] Enable complex floordiv/floormod
expressions in compute_at (#14854)
bd24133fce is described below
commit bd24133fce184c84539461b3dc19fdcf7ad60a9e
Author: Abhikrant Sharma <[email protected]>
AuthorDate: Wed Jun 14 12:06:10 2023 +0530
[TIR][Compute-at] Enable complex floordiv/floormod expressions in
compute_at (#14854)
* [TIR][Compute-at] Enable complex floordiv/floormod expressions in
compute_at
Right now, compute_at is not able to handle movement of blocks if the
indices
of tensors contain complex floordiv/floormod operations.
For ex. if a layout of tensor contains (width % 2) // 2,
compute_at fails with an error.
This change is to handle complex expressions.
* Fix review comments.
* Fix review comments
- Merge conditions for floordiv and floormods respectively to handle
recursion
- Update test case to handle non-bijective index map
- Update new_required to be in between [required_min, +inf]
---
src/tir/schedule/primitive/compute_at.cc | 46 ++++++++++++++-----
.../unittest/test_tir_schedule_compute_at.py | 52 ++++++++++++++++++++++
2 files changed, 87 insertions(+), 11 deletions(-)
diff --git a/src/tir/schedule/primitive/compute_at.cc
b/src/tir/schedule/primitive/compute_at.cc
index b161bf954d..8210274960 100644
--- a/src/tir/schedule/primitive/compute_at.cc
+++ b/src/tir/schedule/primitive/compute_at.cc
@@ -422,19 +422,43 @@ std::pair<Var, BlockVarDomainInfo>
SolveBlockVarDomain(const arith::IntSet& prov
var_dom = arith::IntSet::Interval(required_min, required_max);
var_bound = arith::IntSet::Interval(0, dim_max);
} else {
- arith::PVar<PrimExpr> p_f;
- if ((floordiv(p_v, p_f)).Match(provided_min)) {
- // a <= (x // factor) <= b, fac > 0 ==> (a * fac) <= x <= (b * fac +
fac - 1)
- PrimExpr fac = p_f.Eval();
+ arith::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 (analyzer->CanProveGreaterEqual(fac, 1)) {
- var = p_v.Eval();
- var_dom = arith::IntSet::Interval(required_min * fac,
- analyzer->Simplify(required_max *
fac + fac - 1));
- var_bound = arith::IntSet::Interval(0, analyzer->Simplify(dim_max *
fac + fac - 1));
+ if (var_expr->IsInstance<VarNode>()) {
+ // a <= (x // factor) <= b, fac > 0 ==> (a * fac) <= x <= (b * fac
+ fac - 1)
+ var = Downcast<Var>(var_expr);
+ var_dom = arith::IntSet::Interval(required_min * fac,
+ analyzer->Simplify(required_max
* fac + fac - 1));
+ var_bound = arith::IntSet::Interval(0, analyzer->Simplify(dim_max
* fac + fac - 1));
+ } else {
+ const arith::IntSet new_provided =
arith::IntSet::SinglePoint(p_f1.Eval());
+ const arith::IntSet new_required = arith::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();
+ if (var_expr->IsInstance<VarNode>()) {
+ // generally domain of (x % fac) enforce no constraints to domain of
x
+ Var var_mod = Downcast<Var>(var_expr);
+ return {var_mod, BlockVarDomainInfo()};
+ } else {
+ PrimExpr mod_1 = p_f1.Eval();
+ PrimExpr mod_2 = p_f2.Eval();
+ if (analyzer->CanProveGreaterEqual(mod_1, 1) &&
+ analyzer->CanProveGreaterEqual(mod_2, 1)) {
+ const arith::IntSet new_provided =
arith::IntSet::SinglePoint(p_f1.Eval());
+ if (analyzer->CanProveGreaterEqual(required_min, 0)) {
+ const arith::IntSet new_required =
+ arith::IntSet::Interval(required_min,
arith::SymbolicLimits::pos_inf_);
+ return SolveBlockVarDomain(new_provided, new_required, dim_max,
analyzer);
+ }
+ }
}
- } else if ((floormod(p_v, p_f).Match(provided_min))) {
- // generally domain of (x % fac) enforce no constraints to domain of x
- return {p_v.Eval(), BlockVarDomainInfo()};
}
}
}
diff --git a/tests/python/unittest/test_tir_schedule_compute_at.py
b/tests/python/unittest/test_tir_schedule_compute_at.py
index 7efb4cccc0..a1b4cf1559 100644
--- a/tests/python/unittest/test_tir_schedule_compute_at.py
+++ b/tests/python/unittest/test_tir_schedule_compute_at.py
@@ -995,6 +995,48 @@ def
floordiv_and_floormod_indices_after_reverse_compute_at(a: T.handle, b: T.han
Y[v_i] = temp[v_i // 16, v_i % 16]
[email protected]_func
+def recursive_floordiv_floormod(A: T.Buffer((16, 64, 1, 8, 8, 32), "float32"),
+ C: T.Buffer((3, 512, 512), "float32")) -> None:
+ T.func_attr({"tir.noalias": True})
+ # with T.block("root"):
+ B = T.alloc_buffer((1, 128, 16, 8, 2, 32, 2), "float32")
+ for axis1, axis2, axis3, axis4, axis5, axis6, axis7 in T.grid(1, 128, 16,
8, 2, 32, 2):
+ with T.block("In"):
+ v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6, v_axis7 =
T.axis.remap("SSSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6, axis7])
+ T.reads(A[(v_axis2 * 4 + v_axis5 * 2 + v_axis7) // 32, (v_axis3 *
32 + v_axis6) // 8, (v_axis1 * 8 + v_axis4) // 8, (v_axis3 * 32 + v_axis6) % 8,
v_axis1 * 8 + v_axis4, (v_axis2 * 4 + v_axis5 * 2 + v_axis7) % 32])
+ T.writes(B[v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6,
v_axis7])
+ B[v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6, v_axis7] =
A[(v_axis2 * 4 + v_axis5 * 2 + v_axis7) // 32, (v_axis3 * 32 + v_axis6) // 8,
(v_axis1 * 8 + v_axis4) // 8, (v_axis3 * 32 + v_axis6) % 8, v_axis1 * 8 +
v_axis4, (v_axis2 * 4 + v_axis5 * 2 + v_axis7) % 32] + 3
+ for ax1, ax2, ax3 in T.grid(3, 512, 512):
+ with T.block("Out"):
+ v1, v2, v3 = T.axis.remap("SSS", [ax1, ax2, ax3])
+ T.reads(B[v1 // 8, v2 // 4, v3 // 32, v1, v2 % 4 // 2, v3 % 32, v2
% 2])
+ T.writes(C[v1, v2, v3])
+ C[v1, v2, v3] = B[v1 // 8, v2 // 4, v3 // 32, v1, v2 % 4 // 2, v3
% 32, v2 % 2] * 2
+
+
[email protected]_func
+def recursive_floordiv_floormod_after_reverse_compute_at(A: T.Buffer((16, 64,
1, 8, 8, 32), "float32"), C: T.Buffer((3, 512, 512), "float32")) -> None:
+ T.func_attr({"tir.noalias": T.bool(True)})
+ # with T.block("root"):
+ B = T.alloc_buffer((1, 128, 16, 8, 2, 32, 2))
+ for axis1, axis2, axis3 in T.grid(1, 128, 16):
+ for axis4, axis5, axis6, axis7 in T.grid(8, 2, 32, 2):
+ with T.block("In"):
+ v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6, v_axis7
= T.axis.remap("SSSSSSS", [axis1, axis2, axis3, axis4, axis5, axis6, axis7])
+ T.reads(A[(v_axis2 * 4 + v_axis5 * 2 + v_axis7) // 32,
(v_axis3 * 32 + v_axis6) // 8, (v_axis1 * 8 + v_axis4) // 8, (v_axis3 * 32 +
v_axis6) % 8, v_axis1 * 8 + v_axis4, (v_axis2 * 4 + v_axis5 * 2 + v_axis7) %
32])
+ T.writes(B[v_axis1, v_axis2, v_axis3, v_axis4, v_axis5,
v_axis6, v_axis7])
+ B[v_axis1, v_axis2, v_axis3, v_axis4, v_axis5, v_axis6,
v_axis7] = A[(v_axis2 * 4 + v_axis5 * 2 + v_axis7) // 32, (v_axis3 * 32 +
v_axis6) // 8, (v_axis1 * 8 + v_axis4) // 8, (v_axis3 * 32 + v_axis6) % 8,
v_axis1 * 8 + v_axis4, (v_axis2 * 4 + v_axis5 * 2 + v_axis7) % 32] +
T.float32(3)
+ for ax0, ax1, ax2 in T.grid(3, 4, 32):
+ with T.block("Out"):
+ v1 = T.axis.spatial(3, ax0)
+ v2 = T.axis.spatial(512, axis2 * 4 + ax1)
+ v3 = T.axis.spatial(512, axis3 * 32 + ax2)
+ T.reads(B[v1 // 8, v2 // 4, v3 // 32, v1, v2 % 4 // 2, v3 %
32, v2 % 2])
+ T.writes(C[v1, v2, v3])
+ C[v1, v2, v3] = B[v1 // 8, v2 // 4, v3 // 32, v1, v2 % 4 // 2,
v3 % 32, v2 % 2] * T.float32(2)
+
+
@T.prim_func
def tiled_repeat_op(x: T.Buffer((4,), "float32"), T_repeat: T.Buffer((64,),
"float32")) -> None:
T_add = T.alloc_buffer([4], dtype="float32")
@@ -1255,6 +1297,16 @@ def
test_reverse_compute_at_floordiv_and_floormod_indices(use_block_name):
verify_trace_roundtrip(sch=sch, mod=floordiv_and_floormod_indices)
+def test_reverse_compute_at_floordiv_and_floormod_recursive(use_block_name):
+ sch = tir.Schedule(recursive_floordiv_floormod, debug_mask="all")
+ write_block = sch.get_block("Out")
+ sch.reverse_compute_at(write_block, sch.get_loops("In")[2])
+ tvm.ir.assert_structural_equal(
+ recursive_floordiv_floormod_after_reverse_compute_at, sch.mod["main"]
+ )
+ verify_trace_roundtrip(sch=sch, mod=recursive_floordiv_floormod)
+
+
def test_read_out_of_bound(use_block_name):
sch = tir.Schedule(read_out_of_bound, debug_mask="all")
block = sch.get_block("B")