blob: 7ae40644553042dd25cde97c2fafd171df4eb017 [file]
# 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-function-docstring,missing-module-docstring
import sys
import pytest
import tvm
import tvm.testing
from tvm import tir
from tvm.script import tir as T
from tvm.tir.schedule.testing import (
verify_trace_roundtrip,
assert_structural_equal_ignore_global_symbol,
)
# pylint: disable=no-member,invalid-name,unused-variable
@T.prim_func
def element_wise(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128, 128))
for i, j in T.grid(128, 128):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i, j])
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func
def element_wise_parallelized(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128, 128))
for i0 in T.parallel(0, 128):
for i1 in T.serial(0, 128):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i0, i1])
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func
def element_wise_i_bound(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128, 128))
for i0 in T.thread_binding(0, 128, thread="threadIdx.x"):
for i1 in T.serial(0, 128):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i0, i1])
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func
def element_wise_compute_at_split(a: T.handle, c: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
C = T.match_buffer(c, (128, 128))
B = T.alloc_buffer((128, 128))
for i in T.serial(0, 128):
for j0 in T.serial(0, 128):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i, j0])
B[vi, vj] = A[vi, vj] * 2.0
for j1o, j1i in T.grid(32, 4):
with T.block("C"):
vi = T.axis.S(128, i)
vj = T.axis.S(128, j1o * 4 + j1i)
C[vi, vj] = B[vi, vj] + 1.0
@T.prim_func
def element_wise_compute_at_split_vectorized(a: T.handle, c: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
C = T.match_buffer(c, (128, 128))
B = T.alloc_buffer((128, 128))
for i in T.serial(0, 128):
for j0 in T.serial(0, 128):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i, j0])
B[vi, vj] = A[vi, vj] * 2.0
for j1o in T.serial(0, 32):
for j1i in T.vectorized(0, 4):
with T.block("C"):
vi = T.axis.S(128, i)
vj = T.axis.S(128, j1o * 4 + j1i)
C[vi, vj] = B[vi, vj] + 1.0
@T.prim_func
def element_wise_split_predicate(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, [128, 128])
B = T.match_buffer(b, [128, 128])
for i, j_0, j_1 in T.grid(128, 13, 10):
with T.block("B"):
T.where(j_0 * 10 + j_1 < 128)
vi = T.axis.S(128, i)
vj = T.axis.S(128, j_0 * 10 + j_1)
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func
def element_wise_split_predicate_parallelized(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, [128, 128])
B = T.match_buffer(b, [128, 128])
for i in T.serial(0, 128):
for j_0 in T.parallel(0, 13):
for j_1 in T.serial(0, 10):
with T.block("B"):
T.where(j_0 * 10 + j_1 < 128)
vi = T.axis.S(128, i)
vj = T.axis.S(128, j_0 * 10 + j_1)
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func
def element_wise_split_predicate_vectorized(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, [128, 128])
B = T.match_buffer(b, [128, 128])
for i in T.vectorized(0, 128):
for j_0, j_1 in T.grid(13, 10):
with T.block("B"):
T.where(j_0 * 10 + j_1 < 128)
vi = T.axis.S(128, i)
vj = T.axis.S(128, j_0 * 10 + j_1)
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func
def element_wise_compute_at_split_j0_j1o_bound(a: T.handle, c: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
C = T.match_buffer(c, (128, 128))
B = T.alloc_buffer((128, 128))
for i in T.serial(0, 128):
for j0 in T.thread_binding(0, 128, thread="threadIdx.x"):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i, j0])
B[vi, vj] = A[vi, vj] * 2.0
for j1o in T.thread_binding(0, 32, thread="threadIdx.x"):
for j1i in T.serial(0, 4):
with T.block("C"):
vi = T.axis.S(128, i)
vj = T.axis.S(128, j1o * 4 + j1i)
C[vi, vj] = B[vi, vj] + 1.0
@T.prim_func
def matmul(a: T.handle, b: T.handle, c: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128, 128))
C = T.match_buffer(c, (128, 128))
for i, j, k in T.grid(128, 128, 128):
with T.block("C"):
vi, vj, vk = T.axis.remap("SSR", [i, j, k])
with T.init():
C[vi, vj] = 0.0
C[vi, vj] = C[vi, vj] + A[vi, vk] * B[vj, vk]
@T.prim_func
def rowsum(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128,))
for i, k in T.grid(128, 128):
with T.block("B"):
vi, vk = T.axis.remap("SR", [i, k])
with T.init():
B[vi] = 0.0
B[vi] = B[vi] + A[vi, vk]
@T.prim_func
def rowsum_unrolled(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128,))
for i0 in T.unroll(0, 128):
for i1 in T.serial(0, 128):
with T.block("B"):
vi, vk = T.axis.remap("SR", [i0, i1])
with T.init():
B[vi] = 0.0
B[vi] = B[vi] + A[vi, vk]
@T.prim_func
def rowsum_not_quasi_affine(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128,))
for i, k in T.grid(128, 16):
with T.block("B"):
vi = T.axis.S(128, i)
vk = T.axis.R(128, T.floordiv(k * k, 2))
with T.init():
B[vi] = 0.0
B[vi] = B[vi] + A[vi, vk]
@T.prim_func
def rowsum_not_compact_data_flow(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128,))
for i, k in T.grid(128, 16):
with T.block("B"):
vi, vk = T.axis.remap("SR", [i, k])
with T.init():
B[vk] = 0.0
B[vk] = B[vk] + A[vi, vk]
@T.prim_func
def rowsum_cross_thread_reduction(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, (128, 128))
B = T.match_buffer(b, (128,))
for i0 in T.serial(0, 128):
for i1 in T.thread_binding(0, 128, thread="threadIdx.x"):
with T.block("B"):
vi, vk = T.axis.remap("SR", [i0, i1])
with T.init():
B[vi] = 0.0
B[vi] = B[vi] + A[vi, vk]
@T.prim_func
def opaque_block(a: T.handle) -> None:
A = T.match_buffer(a, (16,))
for i in T.serial(0, 15):
with T.block("opaque"):
A[i + 1] = A[i + 1] + A[i]
@T.prim_func
def block_inside_init(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, [128, 128, 128], dtype="float32")
B = T.match_buffer(b, [128, 128], dtype="float32")
for i in T.serial(0, 128):
with T.block("outer"):
vi = T.axis.S(128, i)
with T.init():
for j in T.serial(0, 128):
with T.block("init"):
vj = T.axis.S(128, j)
B[vi, vj] = 0.0
for k in T.serial(0, 128):
for j in T.serial(0, 128):
with T.block("inner"):
vj, vk = T.axis.remap("SR", [j, k])
B[vi, vj] = B[vi, vj] + A[vi, vj, vk]
@T.prim_func
def thread_bound_block_inside_init(a: T.handle, b: T.handle) -> None:
A = T.match_buffer(a, [128, 128, 128], dtype="float32")
B = T.match_buffer(b, [128, 128], dtype="float32")
for i in T.thread_binding(0, 128, thread="threadIdx.x"):
with T.block("outer"):
vi = T.axis.S(128, i)
with T.init():
for j in T.serial(0, 128):
with T.block("init"):
vj = T.axis.S(128, j)
B[vi, vj] = 0.0
for k in T.serial(0, 128):
for j in T.serial(0, 128):
with T.block("inner"):
vj, vk = T.axis.remap("SR", [j, k])
B[vi, vj] = B[vi, vj] + A[vi, vj, vk]
@T.prim_func
def decomposed_gemm(
A: T.Buffer((16, 16), "float32"),
B: T.Buffer((16, 16), "float32"),
C: T.Buffer((16, 16), "float32"),
):
local = T.alloc_buffer((16, 16), "float32")
for i, j in T.grid(4, 4):
for ii, jj in T.grid(4, 4):
with T.block("init"):
vi = T.axis.S(16, i * 4 + ii)
vj = T.axis.S(16, j * 4 + jj)
local[vi, vj] = 0
for k, ii, jj in T.grid(16, 4, 4):
with T.block("update"):
vi = T.axis.S(16, i * 4 + ii)
vj = T.axis.S(16, j * 4 + jj)
vk = T.axis.R(16, k)
local[vi, vj] += A[vi, vk] * B[vj, vk]
for ii, jj in T.grid(4, 4):
with T.block("C"):
vi = T.axis.S(16, i * 4 + ii)
vj = T.axis.S(16, j * 4 + jj)
C[vi, vj] = local[vi, vj]
@T.prim_func
def decomposed_gemm_after_vectorize(
A: T.Buffer((16, 16), "float32"),
B: T.Buffer((16, 16), "float32"),
C: T.Buffer((16, 16), "float32"),
):
local = T.alloc_buffer((16, 16), "float32")
for i, j in T.grid(4, 4):
for ii, jj in T.grid(4, 4):
with T.block("init"):
vi = T.axis.S(16, i * 4 + ii)
vj = T.axis.S(16, j * 4 + jj)
local[vi, vj] = 0
for k, ii, jj in T.grid(16, 4, 4):
with T.block("update"):
vi = T.axis.S(16, i * 4 + ii)
vj = T.axis.S(16, j * 4 + jj)
vk = T.axis.R(16, k)
local[vi, vj] += A[vi, vk] * B[vj, vk]
for ii in range(4):
for jj in T.vectorized(4):
with T.block("C"):
vi = T.axis.S(16, i * 4 + ii)
vj = T.axis.S(16, j * 4 + jj)
C[vi, vj] = local[vi, vj]
@T.prim_func
def nested_block_bind(
A: T.Buffer((16, 16, 16, 16), "float32"), B: T.Buffer((16, 16, 16), "float32")
):
for i, j in T.grid(16, 16):
with T.block("outer"):
vi, vj = T.axis.remap("SS", [i, j])
for k, l in T.grid(16, 16):
with T.block("inner"):
vk, vl = T.axis.remap("SR", [k, l])
with T.init():
B[vi, vj, vk] = 0.0
B[vi, vj, vk] = B[vi, vj, vk] + A[vi, vj, vk, vl]
@T.prim_func
def thread_bound_nested_block(
A: T.Buffer((16, 16, 16, 16), "float32"), B: T.Buffer((16, 16, 16), "float32")
) -> None:
for i in T.serial(16):
for j in T.thread_binding(16, thread="blockIdx.x"):
with T.block("outer"):
vi, vj = T.axis.remap("SS", [i, j])
for k in T.serial(16):
for l in T.thread_binding(16, thread="threadIdx.x"):
with T.block("inner"):
vk, vl = T.axis.remap("SR", [k, l])
with T.init():
B[vi, vj, vk] = T.float32(0)
B[vi, vj, vk] = B[vi, vj, vk] + A[vi, vj, vk, vl]
@T.prim_func
def nested_block_bind_after_cache_read(
A: T.Buffer((16, 16), "float32"), B: T.Buffer((16,), "float32")
) -> None:
for i in T.serial(16):
with T.block("outer"):
vi = T.axis.spatial(16, i)
A_shared = T.alloc_buffer([1, 16], dtype="float32", scope="shared")
for ax0, ax1 in T.grid(1, 16):
with T.block("A_shared"):
v0 = T.axis.spatial(16, vi + ax0)
v1 = T.axis.spatial(16, ax1)
A_shared[v0, v1] = A[v0, v1]
for j in T.serial(16):
with T.block("inner"):
vj = T.axis.reduce(16, j)
with T.init():
B[vi] = T.float32(0)
B[vi] = B[vi] + A_shared[vi, vj]
@T.prim_func
def thread_bound_nested_block_after_cache_read(
A: T.Buffer((16, 16), "float32"), B: T.Buffer((16,), "float32")
) -> None:
for i in T.thread_binding(16, thread="blockIdx.x"):
with T.block("outer"):
vi = T.axis.spatial(16, i)
A_shared = T.alloc_buffer([1, 16], dtype="float32", scope="shared")
for ax0, ax1 in T.grid(1, 16):
with T.block("A_shared"):
v0 = T.axis.spatial(16, vi + ax0)
v1 = T.axis.spatial(16, ax1)
A_shared[v0, v1] = A[v0, v1]
for j in T.thread_binding(16, thread="threadIdx.x"):
with T.block("inner"):
vj = T.axis.reduce(16, j)
with T.init():
B[vi] = T.float32(0)
B[vi] = B[vi] + A_shared[vi, vj]
@T.prim_func
def decomposed_gemm_parallelize_init(
A: T.Buffer((16, 16), "float32"),
B: T.Buffer((16, 16), "float32"),
C: T.Buffer((16, 16), "float32"),
) -> None:
local = T.alloc_buffer([16, 16], dtype="float32")
for i, j in T.grid(4, 4):
for ii in T.serial(4):
for jj in T.vectorized(4):
with T.block("init"):
vi = T.axis.spatial(16, i * 4 + ii)
vj = T.axis.spatial(16, j * 4 + jj)
T.reads()
T.writes(local[vi, vj])
local[vi, vj] = 0
for k, ii, jj in T.grid(16, 4, 4):
with T.block("update"):
vi = T.axis.spatial(16, i * 4 + ii)
vj = T.axis.spatial(16, j * 4 + jj)
vk = T.axis.reduce(16, k)
T.reads(local[vi, vj], A[vi, vk], B[vj, vk])
T.writes(local[vi, vj])
local[vi, vj] = local[vi, vj] + A[vi, vk] * B[vj, vk]
for ii, jj in T.grid(4, 4):
with T.block("C"):
vi = T.axis.spatial(16, i * 4 + ii)
vj = T.axis.spatial(16, j * 4 + jj)
T.reads(local[vi, vj])
T.writes(C[vi, vj])
C[vi, vj] = local[vi, vj]
@T.prim_func
def scatter_compute(A: T.Buffer((16,), "float32"), B: T.Buffer((16,), "float32")):
for i in T.grid(8):
with T.block("first_half"):
vi = T.axis.spatial(16, 8 + i)
B[vi] = A[vi - 8]
for i in T.grid(8):
with T.block("last_half"):
vi = T.axis.spatial(16, i)
B[vi] = A[vi + 8]
@T.prim_func
def scatter_compute_parallelize(
A: T.Buffer((16,), "float32"), B: T.Buffer((16,), "float32")
) -> None:
# body
# with T.block("root")
for i in T.parallel(8):
with T.block("first_half"):
vi = T.axis.spatial(16, 8 + i)
T.reads(A[vi - 8])
T.writes(B[vi])
B[vi] = A[vi - 8]
for i in T.parallel(8):
with T.block("last_half"):
vi = T.axis.spatial(16, i)
T.reads(A[vi + 8])
T.writes(B[vi])
B[vi] = A[vi + 8]
# pylint: enable=no-member,invalid-name,unused-variable
def test_parallel():
s = tir.Schedule(element_wise, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
s.parallel(i)
assert_structural_equal_ignore_global_symbol(s.mod["main"], element_wise_parallelized)
verify_trace_roundtrip(s, mod=element_wise)
def test_parallel_predicate():
s = tir.Schedule(element_wise_split_predicate, debug_mask="all")
_, j, _ = s.get_loops(s.get_block("B"))
s.parallel(j)
assert_structural_equal_ignore_global_symbol(
s.mod["main"], element_wise_split_predicate_parallelized
)
verify_trace_roundtrip(s, mod=element_wise_split_predicate)
def test_parallel_reduction_block_iter():
s = tir.Schedule(matmul, debug_mask="all")
_, _, k = s.get_loops(s.get_block("C"))
with pytest.raises(tvm.tir.ScheduleError):
s.parallel(k)
def test_parallel_not_quasi_affine():
s = tir.Schedule(rowsum_not_quasi_affine, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
with pytest.raises(tvm.tir.ScheduleError):
s.parallel(i)
def test_parallel_not_compact_data_flow():
s = tir.Schedule(rowsum_not_compact_data_flow, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
with pytest.raises(tvm.tir.ScheduleError):
s.parallel(i)
def test_vectorize():
s = tir.Schedule(element_wise_compute_at_split, debug_mask="all")
_, _, j1i = s.get_loops(s.get_block("C"))
s.vectorize(j1i)
assert_structural_equal_ignore_global_symbol(
s.mod["main"], element_wise_compute_at_split_vectorized
)
verify_trace_roundtrip(s, mod=element_wise_compute_at_split)
def test_vectorize_predicate():
s = tir.Schedule(element_wise_split_predicate, debug_mask="all")
i, _, _ = s.get_loops(s.get_block("B"))
s.vectorize(i)
assert_structural_equal_ignore_global_symbol(
s.mod["main"], element_wise_split_predicate_vectorized
)
verify_trace_roundtrip(s, mod=element_wise_split_predicate)
def test_vectorize_opaque_block():
s = tir.Schedule(opaque_block, debug_mask="all")
(i,) = s.get_loops(s.get_block("opaque"))
with pytest.raises(tvm.tir.ScheduleError):
s.vectorize(i)
def test_unroll():
s = tir.Schedule(rowsum, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
s.unroll(i)
assert_structural_equal_ignore_global_symbol(s.mod["main"], rowsum_unrolled)
verify_trace_roundtrip(s, mod=rowsum)
def test_unroll_after_bind():
s = tir.Schedule(rowsum, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
s.bind(i, "blockIdx.x")
s.unroll(i)
assert_structural_equal_ignore_global_symbol(s.mod["main"], rowsum_unrolled)
verify_trace_roundtrip(s, mod=rowsum)
def test_bind1():
s = tir.Schedule(element_wise, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
s.bind(i, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(s.mod["main"], element_wise_i_bound)
verify_trace_roundtrip(s, mod=element_wise)
def test_bind2():
s = tir.Schedule(element_wise_compute_at_split, debug_mask="all")
_, j0 = s.get_loops(s.get_block("B"))
_, j1o, _ = s.get_loops(s.get_block("C"))
s.bind(j0, "threadIdx.x")
s.bind(j1o, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(
s.mod["main"], element_wise_compute_at_split_j0_j1o_bound
)
verify_trace_roundtrip(s, mod=element_wise_compute_at_split)
def test_bind_cross_thread_reduction():
s = tir.Schedule(rowsum, debug_mask="all")
_, k = s.get_loops(s.get_block("B"))
s.bind(k, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(s.mod["main"], rowsum_cross_thread_reduction)
verify_trace_roundtrip(s, mod=rowsum)
def test_bind_not_cross_thread_reduction():
s = tir.Schedule(rowsum, debug_mask="all")
_, k = s.get_loops(s.get_block("B"))
with pytest.raises(tvm.tir.ScheduleError):
s.bind(k, "blockIdx.x")
def test_bind_after_bind():
s = tir.Schedule(element_wise, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
s.bind(i, "blockIdx.x")
s.bind(i, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(s.mod["main"], element_wise_i_bound)
verify_trace_roundtrip(s, mod=element_wise)
def test_block_inside_init():
s = tir.Schedule(block_inside_init, debug_mask="all")
(i,) = s.get_loops(s.get_block("outer"))
s.bind(i, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(s.mod["main"], thread_bound_block_inside_init)
verify_trace_roundtrip(s, mod=block_inside_init)
def test_vectorize_after_decompose():
s = tir.Schedule(decomposed_gemm, debug_mask="all")
jj = s.get_loops(s.get_block("C"))[-1]
s.vectorize(jj)
assert_structural_equal_ignore_global_symbol(s.mod["main"], decomposed_gemm_after_vectorize)
verify_trace_roundtrip(s, mod=decomposed_gemm)
def test_nested_block_bind():
s = tir.Schedule(nested_block_bind)
block_outer = s.get_block("outer")
block_inner = s.get_block("inner")
_, j = s.get_loops(block_outer)
_, l = s.get_loops(block_inner)
s.bind(l, "threadIdx.x")
s.bind(j, "blockIdx.x")
assert_structural_equal_ignore_global_symbol(s.mod["main"], thread_bound_nested_block)
verify_trace_roundtrip(s, mod=nested_block_bind)
def test_nexted_block_bind_after_cache_read():
s = tir.Schedule(nested_block_bind_after_cache_read)
block_outer = s.get_block("outer")
block_inner = s.get_block("inner")
(i,) = s.get_loops(block_outer)
(j,) = s.get_loops(block_inner)
s.bind(i, "blockIdx.x")
s.bind(j, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(
s.mod["main"], thread_bound_nested_block_after_cache_read
)
verify_trace_roundtrip(s, mod=nested_block_bind_after_cache_read)
def test_vectorize_init():
s = tir.Schedule(decomposed_gemm, debug_mask="all")
init_blk = s.get_block("init")
upd_blk = s.get_block("update")
_, _, ii_0, jj_0 = s.get_loops(init_blk)
_, _, k_1, ii_1, jj_1 = s.get_loops(upd_blk)
s.vectorize(jj_0)
assert_structural_equal_ignore_global_symbol(s.mod["main"], decomposed_gemm_parallelize_init)
verify_trace_roundtrip(s, mod=decomposed_gemm)
def test_scatter_parallelize():
s = tir.Schedule(scatter_compute, debug_mask="all")
first = s.get_block("first_half")
last = s.get_block("last_half")
(i_0,) = s.get_loops(first)
(i_1,) = s.get_loops(last)
s.parallel(i_0)
s.parallel(i_1)
assert_structural_equal_ignore_global_symbol(s.mod["main"], scatter_compute_parallelize)
verify_trace_roundtrip(s, mod=scatter_compute)
def test_bind_thread_iter_var_dtype():
@T.prim_func(private=True)
def before(
A: T.Buffer((T.int64(128), T.int64(128))),
B: T.Buffer((T.int64(128), T.int64(128))),
) -> None:
for i, j in T.grid(T.int64(128), T.int64(128)):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i, j])
B[vi, vj] = A[vi, vj] * 2.0
@T.prim_func(private=True)
def expected(
A: T.Buffer((T.int64(128), T.int64(128))),
B: T.Buffer((T.int64(128), T.int64(128))),
) -> None:
for i0 in T.thread_binding(T.int64(128), thread="threadIdx.x"):
for i1 in range(T.int64(128)):
with T.block("B"):
vi, vj = T.axis.remap("SS", [i0, i1])
B[vi, vj] = A[vi, vj] * 2.0
s = tir.Schedule(before, debug_mask="all")
i, _ = s.get_loops(s.get_block("B"))
s.bind(i, "threadIdx.x")
assert_structural_equal_ignore_global_symbol(s.mod["main"], expected)
verify_trace_roundtrip(s, mod=before)
if __name__ == "__main__":
tvm.testing.main()