mirror of
https://github.com/tile-ai/tilelang.git
synced 2026-10-24 07:04:00 +08:00
* Add runtime checks for host-evaluable assumptions * Preserve nullable buffer semantics for assume checks * Rename assume runtime-check marker
136 lines
3.6 KiB
Python
136 lines
3.6 KiB
Python
import pytest
|
|
import torch
|
|
|
|
import tilelang
|
|
import tilelang.language as T
|
|
import tilelang.testing
|
|
|
|
|
|
def test_assume_remove_boundary_check():
|
|
@tilelang.jit
|
|
def kernel_with_assume():
|
|
N = T.dynamic("N")
|
|
|
|
@T.prim_func
|
|
def main(A: T.Tensor((N,), T.float32), l: T.int32, r: T.int32):
|
|
with T.Kernel(1, threads=32) as _:
|
|
for i in T.serial(r - l + 1):
|
|
T.assume(l + i >= 0 and l + i < N)
|
|
A[l + i] = 0
|
|
|
|
return main
|
|
|
|
jit_kernel = kernel_with_assume()
|
|
source = jit_kernel.get_kernel_source()
|
|
|
|
assert "if (" not in source
|
|
|
|
|
|
def test_assume_enable_vectorization():
|
|
@tilelang.jit
|
|
def kernel_vectorize(M):
|
|
N = T.dynamic("N")
|
|
vectorize_size = 4
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((M, N), T.float32),
|
|
B: T.Tensor((M, N), T.float32),
|
|
):
|
|
with T.Kernel(1, threads=32) as _:
|
|
tid = T.get_thread_binding()
|
|
|
|
base_idx = tid * 4
|
|
T.assume(N % vectorize_size == 0)
|
|
|
|
for i in T.vectorized(vectorize_size):
|
|
T.assume(base_idx + i < N)
|
|
B[tid, base_idx + i] = A[tid, base_idx + i]
|
|
|
|
return main
|
|
|
|
jit_kernel = kernel_vectorize(128)
|
|
source = jit_kernel.get_kernel_source()
|
|
|
|
assert ("float4" in source) and ("if (" not in source)
|
|
|
|
|
|
def test_assume_complex_indexing():
|
|
@tilelang.jit
|
|
def kernel_complex():
|
|
M = T.dynamic("M")
|
|
N = T.dynamic("N")
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((M, N), T.float32),
|
|
B: T.Tensor((M, N), T.float32),
|
|
):
|
|
with T.Kernel(1, threads=32) as _:
|
|
tid = T.get_thread_binding()
|
|
for j in T.serial(N):
|
|
i_src = T.min(j + 233, tid + 2)
|
|
j_src = j * T.ceildiv(j, i_src) * j - 1
|
|
|
|
T.assume(i_src >= 0 and i_src < M)
|
|
T.assume(j_src >= 0 and j_src < N)
|
|
|
|
B[tid, j] = A[i_src, j_src]
|
|
|
|
return main
|
|
|
|
jit_kernel = kernel_complex()
|
|
source = jit_kernel.get_kernel_source()
|
|
|
|
assert "if (" not in source
|
|
|
|
|
|
def test_assume_on_alloc_var():
|
|
# T.assume on a mutable T.alloc_var (local.var) variable is treated as an
|
|
# axiom and must be propagated, removing the out-of-bounds check on A[x].
|
|
@tilelang.jit
|
|
def kernel_alloc_var():
|
|
N = T.dynamic("N")
|
|
|
|
@T.prim_func
|
|
def main(A: T.Tensor((N,), T.float32), out: T.Tensor((1,), T.float32), l: T.int32):
|
|
with T.Kernel(1, threads=32) as _:
|
|
x = T.alloc_var(T.int32)
|
|
x = l
|
|
T.assume(x >= 0 and x < N)
|
|
out[0] = A[x]
|
|
|
|
return main
|
|
|
|
jit_kernel = kernel_alloc_var()
|
|
source = jit_kernel.get_kernel_source()
|
|
|
|
assert "if (" not in source
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
def test_host_evaluable_assume_is_checked_at_runtime():
|
|
@tilelang.jit
|
|
def kernel_with_runtime_check():
|
|
N = T.dynamic("N")
|
|
|
|
@T.prim_func
|
|
def main(A: T.Tensor((N,), T.float32)):
|
|
with T.Kernel(1, threads=32):
|
|
T.assume(N % 4 == 0)
|
|
tx = T.get_thread_binding()
|
|
if tx < N:
|
|
A[tx] = 1.0
|
|
|
|
return main
|
|
|
|
jit_kernel = kernel_with_runtime_check()
|
|
jit_kernel(torch.empty(4, device="cuda"))
|
|
|
|
with pytest.raises(RuntimeError, match="Assume: N % 4 == 0"):
|
|
jit_kernel(torch.empty(2, device="cuda"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
tilelang.testing.main()
|