# 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. # ruff: noqa: F821 """Tests for ``@T.jit`` + ``T.constexpr``.""" from __future__ import annotations import pytest import tvm from tvm.ir import assert_structural_equal from tvm.script import tirx as T def test_int_constexpr_specializes_loop_bound(): @T.jit(private=True) def add( A: T.Buffer((N,), "int32"), B: T.Buffer((N,), "int32"), C: T.Buffer((N,), "int32"), *, N: T.constexpr, ): for i in range(N): C[i] = A[i] + B[i] @T.prim_func(private=True) def expected( A: T.Buffer((128,), "int32"), B: T.Buffer((128,), "int32"), C: T.Buffer((128,), "int32"), ): for i in range(128): C[i] = A[i] + B[i] assert_structural_equal(add.specialize(N=128), expected, map_free_vars=True) def test_constexpr_in_2d_buffer_shape(): @T.jit(private=True) def matadd( A: T.Buffer((M, K), "int32"), B: T.Buffer((M, K), "int32"), C: T.Buffer((M, K), "int32"), *, M: T.constexpr, K: T.constexpr, ): for m in range(M): for k in range(K): C[m, k] = A[m, k] + B[m, k] @T.prim_func(private=True) def expected( A: T.Buffer((4, 8), "int32"), B: T.Buffer((4, 8), "int32"), C: T.Buffer((4, 8), "int32"), ): for m in range(4): for k in range(8): C[m, k] = A[m, k] + B[m, k] assert_structural_equal(matadd.specialize(M=4, K=8), expected, map_free_vars=True) def test_constexpr_in_body_expression(): @T.jit(private=True) def scaled_copy( A: T.Buffer((N,), "int32"), B: T.Buffer((N,), "int32"), *, N: T.constexpr, SCALE: T.constexpr, ): for i in range(N): B[i] = A[i] * SCALE @T.prim_func(private=True) def expected( A: T.Buffer((16,), "int32"), B: T.Buffer((16,), "int32"), ): for i in range(16): B[i] = A[i] * 3 assert_structural_equal(scaled_copy.specialize(N=16, SCALE=3), expected, map_free_vars=True) def test_specialize_cache_returns_same_instance(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, ): for i in range(N): A[i] = 0 a = k.specialize(N=8) b = k.specialize(N=8) assert a is b def test_specialize_different_args_produce_different_funcs(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, ): for i in range(N): A[i] = 0 assert k.specialize(N=8) is not k.specialize(N=16) def test_specialize_missing_constexpr_raises(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, SCALE: T.constexpr, ): for i in range(N): A[i] = SCALE with pytest.raises(TypeError, match="missing"): k.specialize(N=8) def test_specialize_extra_kwarg_raises(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, ): for i in range(N): A[i] = 0 with pytest.raises(TypeError, match="unexpected"): k.specialize(N=8, BOGUS=42) def test_jit_kernel_with_nested_inline_helper(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, ): @T.inline def double(x): A[x] = A[x] * 2 for i in range(N): double(i) @T.prim_func(private=True) def expected( A: T.Buffer((4,), "int32"), ): for i in range(4): A[i] = A[i] * 2 assert_structural_equal(k.specialize(N=4), expected, map_free_vars=True) def test_constexpr_default_value(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, SCALE: T.constexpr = 7, ): for i in range(N): A[i] = SCALE @T.prim_func(private=True) def expected( A: T.Buffer((8,), "int32"), ): for i in range(8): A[i] = 7 assert_structural_equal(k.specialize(N=8), expected, map_free_vars=True) # Override the default overridden = k.specialize(N=8, SCALE=99) assert k.specialize(N=8) is not overridden def test_specialize_returns_primfunc(): @T.jit(private=True) def k( A: T.Buffer((N,), "int32"), *, N: T.constexpr, ): for i in range(N): A[i] = 0 spec = k.specialize(N=8) assert isinstance(spec, tvm.tirx.PrimFunc) # Specialized PrimFunc has only the runtime params (constexpr stripped). assert len(spec.params) == 1 if __name__ == "__main__": pytest.main([__file__, "-v"])