# 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: E501, F401, F841 import sys import numpy as np import pytest import tvm import tvm.testing from tvm import tirx from tvm.script import ir as I from tvm.script import relax as R from tvm.script import tirx as T def opt_gemm_lower(): @tvm.script.ir_module class Module: @T.prim_func(s_tir=True) def mmult(A: T.handle, B: T.handle, C: T.handle) -> None: # function attr dict T.func_attr({"tirx.noalias": True}) A_1 = T.match_buffer(A, [16384], elem_offset=0, align=64, offset_factor=1) B_1 = T.match_buffer(B, [1024, 1024], elem_offset=0, align=64, offset_factor=1) C_1 = T.match_buffer(C, [16384], elem_offset=0, align=64, offset_factor=1) # body packedB = T.alloc_buffer((32768,)) for x in T.parallel(0, 32): for y in T.serial(0, 1024): packedB[T.ramp(((x * 32768) + (y * 32)), 1, 32)] = B_1[y, T.ramp(x * 32, 1, 32)] for x_outer in T.parallel(0, 32): C_global = T.alloc_buffer((1024,)) for y_outer in T.serial(0, 32): for x_c_init in T.serial(0, 32): C_global[T.ramp((x_c_init * 32), 1, 32)] = T.broadcast(T.float32(0), 32) for k_outer in T.serial(0, 256): for x_c in T.serial(0, 32): C_global[T.ramp((x_c * 32), 1, 32)] = C_global[ T.ramp((x_c * 32), 1, 32) ] + ( T.broadcast( A_1[(((x_outer * 32768) + (x_c * 1024)) + (k_outer * 4))], 32, ) * packedB[T.ramp(((y_outer * 32768) + (k_outer * 128)), 1, 32)] ) C_global[T.ramp((x_c * 32), 1, 32)] = C_global[ T.ramp((x_c * 32), 1, 32) ] + ( T.broadcast( A_1[ ((((x_outer * 32768) + (x_c * 1024)) + (k_outer * 4)) + 1), ], 32, ) * packedB[ T.ramp((((y_outer * 32768) + (k_outer * 128)) + 32), 1, 32) ] ) C_global[T.ramp((x_c * 32), 1, 32)] = C_global[ T.ramp((x_c * 32), 1, 32) ] + ( T.broadcast( A_1[ ((((x_outer * 32768) + (x_c * 1024)) + (k_outer * 4)) + 2), ], 32, ) * packedB[ T.ramp((((y_outer * 32768) + (k_outer * 128)) + 64), 1, 32) ] ) C_global[T.ramp((x_c * 32), 1, 32)] = C_global[ T.ramp((x_c * 32), 1, 32) ] + ( T.broadcast( A_1[ ((((x_outer * 32768) + (x_c * 1024)) + (k_outer * 4)) + 3), ], 32, ) * packedB[ T.ramp((((y_outer * 32768) + (k_outer * 128)) + 96), 1, 32) ] ) for x_inner in T.serial(0, 32): for y_inner in T.serial(0, 32): C_1[ ( (((x_outer * 32768) + (x_inner * 1024)) + (y_outer * 32)) + y_inner ) ] = C_global[((x_inner * 32) + y_inner)] return Module def launch_env_thread(): @T.prim_func(s_tir=True) def main(inputs: T.Buffer((64, 2, 4), "float32")) -> None: bx = T.launch_thread("blockIdx.x", 64) for i, j in T.grid(2, 4): T.evaluate(inputs[bx, i, j]) return main def opt_conv_tensorcore_lower(): @T.prim_func(s_tir=True) def func( A: T.Buffer((16, 14, 14, 16, 16, 16), "float16"), W: T.Buffer((3, 3, 16, 32, 16, 16), "float16"), Conv: T.Buffer((16, 14, 14, 32, 16, 16), "float32"), ) -> None: # function attr dict T.func_attr({"global_symbol": "default_function", "tirx.noalias": True}) # body A_1 = T.decl_buffer([12845056], dtype="float16", data=A.data) W_1 = T.decl_buffer([1179648], dtype="float16", data=W.data) Conv_1 = T.decl_buffer([25690112], data=Conv.data) bx = T.env_thread("blockIdx.x") by = T.env_thread("blockIdx.y") bz = T.env_thread("blockIdx.z") tx = T.env_thread("threadIdx.x") ty = T.env_thread("threadIdx.y") tz = T.env_thread("threadIdx.z") T.launch_thread(bz, 196) Conv_wmma_accumulator = T.alloc_buffer((2048,), scope="wmma.accumulator") Apad_shared = T.alloc_buffer((12288,), "float16", scope="shared") W_shared = T.alloc_buffer((12288,), "float16", scope="shared") Apad_shared_wmma_matrix_a = T.alloc_buffer((512,), "float16", scope="wmma.matrix_a") W_shared_wmma_matrix_b = T.alloc_buffer((1024,), "float16", scope="wmma.matrix_b") T.launch_thread(bx, 2) T.launch_thread(by, 4) T.launch_thread(ty, 4) T.launch_thread(tz, 2) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 0, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 1, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 2, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 3, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 4, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 5, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 6, T.float32(0), dtype="handle" ) ) T.evaluate( T.tvm_fill_fragment( Conv_wmma_accumulator.data, 16, 16, 16, 7, T.float32(0), dtype="handle" ) ) for ic_outer in T.serial(0, 8): for kh in T.serial(0, 3): for ax2 in T.serial(0, 3): with T.launch_thread(tx, 32): Apad_shared[((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61440 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 32)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61408 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 64)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61376 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 96)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61344 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 128)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61312 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 160)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61280 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 192)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61248 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 224)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61216 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 256)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61184 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 288)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61152 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 320)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61120 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 352)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61088 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 384)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61056 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 416)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 61024 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 448)] = ( T.if_then_else( ( ( ( 1 <= (T.floordiv(bz, 14) + kh) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 60992 ), ], T.float16(0), dtype="float16", ) ) T.launch_thread(tx, 32) Apad_shared[(((((ty * 3072) + (tz * 1536)) + (ax2 * 512)) + tx) + 480)] = ( T.if_then_else( ( ( ( (1 <= (T.floordiv(bz, 14) + kh)) and ((T.floordiv(bz, 14) + kh) < 15) ) and (1 <= (ax2 + T.floormod(bz, 14))) ) and ((ax2 + T.floormod(bz, 14)) < 15) ), A_1[ ( ( ( ( ( ( ( ((bx * 6422528) + (ty * 1605632)) + (tz * 802816) ) + (kh * 57344) ) + (bz * 4096) ) + (ax2 * 4096) ) + (ic_outer * 512) ) + tx ) - 60960 ), ], T.float16(0), dtype="float16", ) ) with T.launch_thread(tx, 32): W_shared[T.ramp((((ty * 512) + (tz * 256)) + (tx * 8)), 1, 8)] = W_1[ T.ramp( ( ( ( (((kh * 393216) + (ic_outer * 16384)) + (by * 2048)) + (ty * 512) ) + (tz * 256) ) + (tx * 8) ), 1, 8, ) ] with T.launch_thread(tx, 32): W_shared[T.ramp(((((ty * 512) + (tz * 256)) + (tx * 8)) + 2048), 1, 8)] = W_1[ T.ramp( ( ( ( ( (((kh * 393216) + (ic_outer * 16384)) + (by * 2048)) + (ty * 512) ) + (tz * 256) ) + (tx * 8) ) + 8192 ), 1, 8, ) ] with T.launch_thread(tx, 32): W_shared[T.ramp(((((ty * 512) + (tz * 256)) + (tx * 8)) + 4096), 1, 8)] = W_1[ T.ramp( ( ( ( ( (((kh * 393216) + (ic_outer * 16384)) + (by * 2048)) + (ty * 512) ) + (tz * 256) ) + (tx * 8) ) + 131072 ), 1, 8, ) ] with T.launch_thread(tx, 32): W_shared[T.ramp(((((ty * 512) + (tz * 256)) + (tx * 8)) + 6144), 1, 8)] = W_1[ T.ramp( ( ( ( ( (((kh * 393216) + (ic_outer * 16384)) + (by * 2048)) + (ty * 512) ) + (tz * 256) ) + (tx * 8) ) + 139264 ), 1, 8, ) ] with T.launch_thread(tx, 32): W_shared[T.ramp(((((ty * 512) + (tz * 256)) + (tx * 8)) + 8192), 1, 8)] = W_1[ T.ramp( ( ( ( ( (((kh * 393216) + (ic_outer * 16384)) + (by * 2048)) + (ty * 512) ) + (tz * 256) ) + (tx * 8) ) + 262144 ), 1, 8, ) ] with T.launch_thread(tx, 32): W_shared[T.ramp(((((ty * 512) + (tz * 256)) + (tx * 8)) + 10240), 1, 8)] = W_1[ T.ramp( ( ( ( ( (((kh * 393216) + (ic_outer * 16384)) + (by * 2048)) + (ty * 512) ) + (tz * 256) ) + (tx * 8) ) + 270336 ), 1, 8, ) ] for ic_inner in T.serial(0, 2): for kw in T.serial(0, 3): T.evaluate( T.tvm_load_matrix_sync( Apad_shared_wmma_matrix_a.data, 16, 16, 16, 0, T.tvm_access_ptr( T.type_annotation(dtype="float16"), Apad_shared.data, (((ty * 3072) + (kw * 512)) + (ic_inner * 256)), 256, 1, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_load_matrix_sync( Apad_shared_wmma_matrix_a.data, 16, 16, 16, 1, T.tvm_access_ptr( T.type_annotation(dtype="float16"), Apad_shared.data, ((((ty * 3072) + (kw * 512)) + (ic_inner * 256)) + 1536), 256, 1, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_load_matrix_sync( W_shared_wmma_matrix_b.data, 16, 16, 16, 0, T.tvm_access_ptr( T.type_annotation(dtype="float16"), W_shared.data, (((kw * 4096) + (ic_inner * 2048)) + (tz * 1024)), 256, 1, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_load_matrix_sync( W_shared_wmma_matrix_b.data, 16, 16, 16, 1, T.tvm_access_ptr( T.type_annotation(dtype="float16"), W_shared.data, ((((kw * 4096) + (ic_inner * 2048)) + (tz * 1024)) + 256), 256, 1, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_load_matrix_sync( W_shared_wmma_matrix_b.data, 16, 16, 16, 2, T.tvm_access_ptr( T.type_annotation(dtype="float16"), W_shared.data, ((((kw * 4096) + (ic_inner * 2048)) + (tz * 1024)) + 512), 256, 1, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_load_matrix_sync( W_shared_wmma_matrix_b.data, 16, 16, 16, 3, T.tvm_access_ptr( T.type_annotation(dtype="float16"), W_shared.data, ((((kw * 4096) + (ic_inner * 2048)) + (tz * 1024)) + 768), 256, 1, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 0, Apad_shared_wmma_matrix_a.data, 0, W_shared_wmma_matrix_b.data, 0, Conv_wmma_accumulator.data, 0, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 1, Apad_shared_wmma_matrix_a.data, 0, W_shared_wmma_matrix_b.data, 1, Conv_wmma_accumulator.data, 1, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 2, Apad_shared_wmma_matrix_a.data, 0, W_shared_wmma_matrix_b.data, 2, Conv_wmma_accumulator.data, 2, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 3, Apad_shared_wmma_matrix_a.data, 0, W_shared_wmma_matrix_b.data, 3, Conv_wmma_accumulator.data, 3, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 4, Apad_shared_wmma_matrix_a.data, 1, W_shared_wmma_matrix_b.data, 0, Conv_wmma_accumulator.data, 4, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 5, Apad_shared_wmma_matrix_a.data, 1, W_shared_wmma_matrix_b.data, 1, Conv_wmma_accumulator.data, 5, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 6, Apad_shared_wmma_matrix_a.data, 1, W_shared_wmma_matrix_b.data, 2, Conv_wmma_accumulator.data, 6, dtype="handle", ) ) T.evaluate( T.tvm_mma_sync( Conv_wmma_accumulator.data, 7, Apad_shared_wmma_matrix_a.data, 1, W_shared_wmma_matrix_b.data, 3, Conv_wmma_accumulator.data, 7, dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 0, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 1, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 256 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 2, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 512 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 3, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 768 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 4, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 1605632 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 5, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 1605888 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 6, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 1606144 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) T.evaluate( T.tvm_store_matrix_sync( Conv_wmma_accumulator.data, 16, 16, 16, 7, T.tvm_access_ptr( T.type_annotation(dtype="float32"), Conv_1.data, ( ( ((((bx * 12845056) + (ty * 3211264)) + (bz * 8192)) + (by * 2048)) + (tz * 1024) ) + 1606400 ), 256, 2, dtype="handle", ), 16, "row_major", dtype="handle", ) ) return func def opt_conv_tensorcore_mod_host(): @T.prim_func(s_tir=True) def opt_conv_tensorcore_mod_host( args: T.handle, arg_type_ids: T.Buffer((3,), "int32"), num_args: T.int32, out_ret_value: T.handle, out_ret_tcode: T.handle, resource_handle: T.handle, ) -> T.int32: # function attr dict T.func_attr( { "tirx.noalias": True, "global_symbol": "default_function", "tirx.is_entry_func": True, "calling_conv": 1, } ) # body stack_tcode_data: T.let[T.handle("int32")] = T.tvm_stack_alloca( "arg_tcode", 10, dtype="handle" ) stack_tcode = T.decl_buffer([9], "int32", data=stack_tcode_data) stack_value: T.let[T.handle] = T.tvm_stack_alloca("arg_value", 10, dtype="handle") assert num_args == 3, "default_function: num_args should be 3" arg0: T.let[T.handle] = T.tvm_struct_get(args, 0, 12, dtype="handle") arg0_code: T.let[T.int32] = arg_type_ids[0] arg1: T.let[T.handle] = T.tvm_struct_get(args, 1, 12, dtype="handle") arg1_code: T.let[T.int32] = arg_type_ids[1] arg2: T.let[T.handle] = T.tvm_struct_get(args, 2, 12, dtype="handle") arg2_code: T.let[T.int32] = arg_type_ids[2] A: T.let[T.handle] = T.tvm_struct_get(arg0, 0, 1, dtype="handle") T.attr(A, "storage_alignment", 128) arg0_shape_data: T.let[T.handle("int64")] = T.tvm_struct_get( arg0, 0, 2, dtype=T.handle("int64").ty ) arg0_shape = T.decl_buffer([6], "int64", data=arg0_shape_data) arg0_strides_data: T.let[T.handle("int64")] = T.tvm_struct_get( arg0, 0, 3, dtype=T.handle("int64").ty ) arg0_strides = T.decl_buffer([6], "int64", data=arg0_strides_data) dev_id: T.let[T.int32] = T.tvm_struct_get(arg0, 0, 9, dtype="int32") W: T.let[T.handle] = T.tvm_struct_get(arg1, 0, 1, dtype="handle") T.attr(W, "storage_alignment", 128) arg1_shape_data: T.let[T.handle("int64")] = T.tvm_struct_get( arg1, 0, 2, dtype=T.handle("int64").ty ) arg1_shape = T.decl_buffer([6], "int64", data=arg1_shape_data) arg1_strides_data: T.let[T.handle("int64")] = T.tvm_struct_get( arg1, 0, 3, dtype=T.handle("int64").ty ) arg1_strides = T.decl_buffer([6], "int64", data=arg1_strides_data) Conv: T.let[T.handle] = T.tvm_struct_get(arg2, 0, 1, dtype="handle") T.attr(Conv, "storage_alignment", 128) arg2_shape_data: T.let[T.handle("int64")] = T.tvm_struct_get( arg2, 0, 2, dtype=T.handle("int64").ty ) arg2_shape = T.decl_buffer([6], "int64", data=arg2_shape_data) arg2_strides_data: T.let[T.handle("int64")] = T.tvm_struct_get( arg2, 0, 3, dtype=T.handle("int64").ty ) arg2_strides = T.decl_buffer([6], "int64", data=arg2_strides_data) assert (((arg0_code == 3) or (arg0_code == 13)) or (arg0_code == 7)) or (arg0_code == 4), ( "default_function: Expect arg[0] to be pointer" ) assert (((arg1_code == 3) or (arg1_code == 13)) or (arg1_code == 7)) or (arg1_code == 4), ( "default_function: Expect arg[1] to be pointer" ) assert (((arg2_code == 3) or (arg2_code == 13)) or (arg2_code == 7)) or (arg2_code == 4), ( "default_function: Expect arg[2] to be pointer" ) assert 6 == T.tvm_struct_get(arg0, 0, 4, dtype="int32"), "arg0.ndim is expected to equal 6" assert 6 == T.tvm_struct_get(arg0, 0, 4, dtype="int32"), "arg0.ndim is expected to equal 6" assert ( (T.tvm_struct_get(arg0, 0, 5, dtype="uint8") == T.uint8(2)) and (T.tvm_struct_get(arg0, 0, 6, dtype="uint8") == T.uint8(16)) ) and (T.tvm_struct_get(arg0, 0, 7, dtype="uint16") == T.uint16(1)), ( "arg0.dtype is expected to be float16" ) assert 16 == T.cast(arg0_shape[0], "int32"), ( "Argument arg0.shape[0] has an unsatisfied constraint" ) assert 14 == T.cast(arg0_shape[1], "int32"), ( "Argument arg0.shape[1] has an unsatisfied constraint" ) assert 14 == T.cast(arg0_shape[2], "int32"), ( "Argument arg0.shape[2] has an unsatisfied constraint" ) assert 16 == T.cast(arg0_shape[3], "int32"), ( "Argument arg0.shape[3] has an unsatisfied constraint" ) assert 16 == T.cast(arg0_shape[4], "int32"), ( "Argument arg0.shape[4] has an unsatisfied constraint" ) assert 16 == T.cast(arg0_shape[5], "int32"), ( "Argument arg0.shape[5] has an unsatisfied constraint" ) if not (T.isnullptr(arg0_strides.data, dtype="bool")): assert ( ( ( ( (1 == T.cast(arg0_strides[5], "int32")) and (16 == T.cast(arg0_strides[4], "int32")) ) and (256 == T.cast(arg0_strides[3], "int32")) ) and (4096 == T.cast(arg0_strides[2], "int32")) ) and (57344 == T.cast(arg0_strides[1], "int32")) ) and (802816 == T.cast(arg0_strides[0], "int32")), ( "arg0.strides: expected to be compact array" ) T.evaluate(0) assert T.uint64(0) == T.tvm_struct_get(arg0, 0, 8, dtype="uint64"), ( "Argument arg0.byte_offset has an unsatisfied constraint" ) assert 2 == T.tvm_struct_get(arg0, 0, 10, dtype="int32"), ( "Argument arg0.device_type has an unsatisfied constraint" ) assert 6 == T.tvm_struct_get(arg1, 0, 4, dtype="int32"), "arg1.ndim is expected to equal 6" assert 6 == T.tvm_struct_get(arg1, 0, 4, dtype="int32"), "arg1.ndim is expected to equal 6" assert ( (T.tvm_struct_get(arg1, 0, 5, dtype="uint8") == T.uint8(2)) and (T.tvm_struct_get(arg1, 0, 6, dtype="uint8") == T.uint8(16)) ) and (T.tvm_struct_get(arg1, 0, 7, dtype="uint16") == T.uint16(1)), ( "arg1.dtype is expected to be float16" ) assert 3 == T.cast(arg1_shape[0], "int32"), ( "Argument arg1.shape[0] has an unsatisfied constraint" ) assert 3 == T.cast(arg1_shape[1], "int32"), ( "Argument arg1.shape[1] has an unsatisfied constraint" ) assert 16 == T.cast(arg1_shape[2], "int32"), ( "Argument arg1.shape[2] has an unsatisfied constraint" ) assert 32 == T.cast(arg1_shape[3], "int32"), ( "Argument arg1.shape[3] has an unsatisfied constraint" ) assert 16 == T.cast(arg1_shape[4], "int32"), ( "Argument arg1.shape[4] has an unsatisfied constraint" ) assert 16 == T.cast(arg1_shape[5], "int32"), ( "Argument arg1.shape[5] has an unsatisfied constraint" ) if not (T.isnullptr(arg1_strides.data, dtype="bool")): assert ( ( ( ( (1 == T.cast(arg1_strides[5], "int32")) and (16 == T.cast(arg1_strides[4], "int32")) ) and (256 == T.cast(arg1_strides[3], "int32")) ) and (8192 == T.cast(arg1_strides[2], "int32")) ) and (131072 == T.cast(arg1_strides[1], "int32")) ) and (393216 == T.cast(arg1_strides[0], "int32")), ( "arg1.strides: expected to be compact array" ) T.evaluate(0) assert T.uint64(0) == T.tvm_struct_get(arg1, 0, 8, dtype="uint64"), ( "Argument arg1.byte_offset has an unsatisfied constraint" ) assert 2 == T.tvm_struct_get(arg1, 0, 10, dtype="int32"), ( "Argument arg1.device_type has an unsatisfied constraint" ) assert dev_id == T.tvm_struct_get(arg1, 0, 9, dtype="int32"), ( "Argument arg1.device_id has an unsatisfied constraint" ) assert 6 == T.tvm_struct_get(arg2, 0, 4, dtype="int32"), "arg2.ndim is expected to equal 6" assert 6 == T.tvm_struct_get(arg2, 0, 4, dtype="int32"), "arg2.ndim is expected to equal 6" assert ( (T.tvm_struct_get(arg2, 0, 5, dtype="uint8") == T.uint8(2)) and (T.tvm_struct_get(arg2, 0, 6, dtype="uint8") == T.uint8(32)) ) and (T.tvm_struct_get(arg2, 0, 7, dtype="uint16") == T.uint16(1)), ( "arg2.dtype is expected to be float32" ) assert 16 == T.cast(arg2_shape[0], "int32"), ( "Argument arg2.shape[0] has an unsatisfied constraint" ) assert 14 == T.cast(arg2_shape[1], "int32"), ( "Argument arg2.shape[1] has an unsatisfied constraint" ) assert 14 == T.cast(arg2_shape[2], "int32"), ( "Argument arg2.shape[2] has an unsatisfied constraint" ) assert 32 == T.cast(arg2_shape[3], "int32"), ( "Argument arg2.shape[3] has an unsatisfied constraint" ) assert 16 == T.cast(arg2_shape[4], "int32"), ( "Argument arg2.shape[4] has an unsatisfied constraint" ) assert 16 == T.cast(arg2_shape[5], "int32"), ( "Argument arg2.shape[5] has an unsatisfied constraint" ) if not (T.isnullptr(arg2_strides.data, dtype="bool")): assert ( ( ( ( (1 == T.cast(arg2_strides[5], "int32")) and (16 == T.cast(arg2_strides[4], "int32")) ) and (256 == T.cast(arg2_strides[3], "int32")) ) and (8192 == T.cast(arg2_strides[2], "int32")) ) and (114688 == T.cast(arg2_strides[1], "int32")) ) and (1605632 == T.cast(arg2_strides[0], "int32")), ( "arg2.strides: expected to be compact array" ) T.evaluate(0) assert T.uint64(0) == T.tvm_struct_get(arg2, 0, 8, dtype="uint64"), ( "Argument arg2.byte_offset has an unsatisfied constraint" ) assert 2 == T.tvm_struct_get(arg2, 0, 10, dtype="int32"), ( "Argument arg2.device_type has an unsatisfied constraint" ) assert dev_id == T.tvm_struct_get(arg2, 0, 9, dtype="int32"), ( "Argument arg2.device_id has an unsatisfied constraint" ) T.evaluate(T.tvm_struct_set(stack_value, 0, 12, T.cast(2, "int64"), dtype="int32")) stack_tcode[0] = 0 T.evaluate(T.tvm_struct_set(stack_value, 1, 12, T.cast(dev_id, "int64"), dtype="int32")) stack_tcode[1] = 0 T.evaluate( T.tvm_call_packed_lowered( "__tvm_set_device", stack_value, stack_tcode.data, 0, 2, dtype="int32" ) ) T.attr(0, "compute_scope", "default_function_compute_") T.evaluate(T.tvm_struct_set(stack_value, 0, 12, A, dtype="int32")) stack_tcode[0] = 3 T.evaluate(T.tvm_struct_set(stack_value, 1, 12, W, dtype="int32")) stack_tcode[1] = 3 T.evaluate(T.tvm_struct_set(stack_value, 2, 12, Conv, dtype="int32")) stack_tcode[2] = 3 T.evaluate(T.tvm_struct_set(stack_value, 3, 12, T.cast(196, "int64"), dtype="int32")) stack_tcode[3] = 0 T.evaluate(T.tvm_struct_set(stack_value, 4, 12, T.cast(2, "int64"), dtype="int32")) stack_tcode[4] = 0 T.evaluate(T.tvm_struct_set(stack_value, 5, 12, T.cast(4, "int64"), dtype="int32")) stack_tcode[5] = 0 T.evaluate(T.tvm_struct_set(stack_value, 6, 12, T.cast(4, "int64"), dtype="int32")) stack_tcode[6] = 0 T.evaluate(T.tvm_struct_set(stack_value, 7, 12, T.cast(2, "int64"), dtype="int32")) stack_tcode[7] = 0 T.evaluate(T.tvm_struct_set(stack_value, 8, 12, T.cast(32, "int64"), dtype="int32")) stack_tcode[8] = 0 T.evaluate( T.tvm_call_packed_lowered( "default_function_kernel0", stack_value, stack_tcode.data, 0, 9, dtype="int32" ) ) return opt_conv_tensorcore_mod_host def vthread_func(): @T.prim_func(s_tir=True) def vthread_func(a: T.handle, c: T.handle) -> None: A = T.match_buffer(a, [256], "float32") C = T.match_buffer(c, [256], "float32") i0 = T.env_thread("blockIdx.x") i1 = T.env_thread("threadIdx.x") i2 = T.env_thread("vthread") T.launch_thread(i0, 4) T.launch_thread(i1, 2) T.launch_thread(i2, 2) B = T.alloc_buffer((16,), scope="local") for j in range(16): B[j] = A[i0 * 64 + i1 * 32 + i2 * 16 + j] + T.float32(1) for j in range(16): C[i0 * 64 + i1 * 32 + i2 * 16 + j] = B[j] * T.float32(2) return vthread_func def matmul(): @T.prim_func(s_tir=True) 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.sblock("update"): vi, vj, vk = T.axis.remap("SSR", [i, j, k]) with T.init(): C[vi, vj] = T.float32(0) C[vi, vj] = C[vi, vj] + A[vi, vk] * B[vj, vk] return matmul def matmul_original(): @T.prim_func(s_tir=True) def matmul_original(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 in T.grid(128, 128): with T.sblock("init"): vi, vj = T.axis.remap("SS", [i, j]) C[vi, vj] = T.float32(0) for k in range(128): with T.sblock("update"): vi, vj, vk = T.axis.remap("SSR", [i, j, k]) C[vi, vj] = C[vi, vj] + A[vi, vk] * B[vj, vk] return matmul_original def element_wise(): @T.prim_func(s_tir=True) def element_wise(a: T.handle, c: T.handle) -> None: A = T.match_buffer(a, (128, 128), "float32") C = T.match_buffer(c, (128, 128), "float32") B = T.sblock_alloc_buffer((128, 128), "float32") for i, j in T.grid(128, 128): with T.sblock("B"): vi, vj = T.axis.remap("SS", [i, j]) B[vi, vj] = A[vi, vj] * T.float32(2) for i, j in T.grid(128, 128): with T.sblock("C"): vi, vj = T.axis.remap("SS", [i, j]) C[vi, vj] = B[vi, vj] + T.float32(1) return element_wise def predicate(): @T.prim_func(s_tir=True) def predicate(b: T.handle, c: T.handle) -> None: B = T.match_buffer(b, (16, 16), "float32") C = T.match_buffer(c, (16, 16), "float32") for i, jo, ji in T.grid(16, 4, 5): with T.sblock("update"): vi = T.axis.S(16, i) vj = T.axis.S(16, jo * 4 + ji) T.where(jo * 4 + ji < 16) C[vi, vj] = B[vi, vj] + T.float32(1) return predicate def test_module_define(): func1 = tvm.ir.IRModule({"matmul": matmul()})["matmul"] func2 = tvm.ir.IRModule({"element_wise": element_wise()})["element_wise"] func3 = tvm.ir.IRModule({"predicate": predicate()})["predicate"] mod1 = tvm.ir.IRModule({"func1": func1, "func2": func2, "func3": func3}) mod2 = tvm.ir.IRModule({"func1": matmul(), "func2": element_wise(), "func3": predicate()}) tvm.ir.assert_structural_equal(mod1, mod2) def test_matmul_original(): func = matmul_original() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) assert isinstance(rt_func.body.block, tirx.stmt.SBlock) assert isinstance(rt_func.body.block.body, tirx.stmt.For) assert isinstance(rt_func.body.block.body.body, tirx.stmt.For) assert isinstance(rt_func.body.block.body.body.body, tirx.stmt.SeqStmt) assert isinstance(rt_func.body.block.body.body.body[0].block, tirx.stmt.SBlock) assert isinstance(rt_func.body.block.body.body.body[1], tirx.stmt.For) assert isinstance(rt_func.body.block.body.body.body[1].body.block, tirx.stmt.SBlock) def test_element_wise(): func = element_wise() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) assert isinstance(rt_func.body.block, tirx.stmt.SBlock) assert isinstance(rt_func.body.block.body, tirx.stmt.SeqStmt) assert isinstance(rt_func.body.block.body[0], tirx.stmt.For) assert isinstance(rt_func.body.block.body[0].body, tirx.stmt.For) assert isinstance(rt_func.body.block.body[0].body.body.block, tirx.stmt.SBlock) assert isinstance(rt_func.body.block.body[1], tirx.stmt.For) assert isinstance(rt_func.body.block.body[1].body, tirx.stmt.For) assert isinstance(rt_func.body.block.body[1].body.body.block, tirx.stmt.SBlock) def test_predicate(): func = predicate() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) assert isinstance(rt_func.body.block, tirx.stmt.SBlock) assert isinstance(rt_func.body.block.body, tirx.stmt.For) assert isinstance(rt_func.body.block.body.body, tirx.stmt.For) assert isinstance(rt_func.body.block.body.body.body, tirx.stmt.For) assert isinstance(rt_func.body.block.body.body.body.body.block, tirx.stmt.SBlock) def for_thread_binding(): @T.prim_func(s_tir=True) def for_thread_binding(a: T.handle, b: T.handle) -> None: A = T.match_buffer(a, (16, 16), "float32") B = T.match_buffer(b, (16, 16), "float32") for i in T.thread_binding(0, 16, thread="threadIdx.x"): for j in T.thread_binding( 0, 16, thread="threadIdx.y", annotations={"attr_key": "attr_value"} ): A[i, j] = B[i, j] + T.float32(1) return for_thread_binding def test_for_thread_binding(): func = for_thread_binding() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) assert isinstance(rt_func.body, tirx.stmt.For) assert rt_func.body.kind == 4 assert rt_func.body.thread_binding.thread_tag == "threadIdx.x" assert isinstance(rt_func.body.body, tirx.stmt.For) assert rt_func.body.body.kind == 4 assert rt_func.body.body.thread_binding.thread_tag == "threadIdx.y" assert rt_func.body.body.annotations["attr_key"] == "attr_value" def match_buffer_region(): @T.prim_func(s_tir=True) def match_buffer_region(a: T.handle, b: T.handle) -> None: A = T.match_buffer(a, (16, 16, 16), "float32") B = T.match_buffer(b, (1), "float32") for i, j in T.grid(16, 4): with T.sblock(): vi, vj = T.axis.remap("SS", [i, j]) C = T.match_buffer(A[0:16, vi, vj * 4 : vj * 4 + 4], (16, 1, 4)) for ii in range(4): with T.sblock(): vii = T.axis.S(4, ii) D = T.match_buffer(C[vii * 4 : vii * 4 + 4, 0, 0:4], (4, 1, 4)) for i, j in T.grid(4, 4): B[0] += D[i, 0, j] return match_buffer_region def test_match_buffer_region(): func = match_buffer_region() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) assert isinstance(rt_func.body, tirx.stmt.SBlockRealize) root = rt_func.body.block assert isinstance(root.body, tirx.stmt.For) assert isinstance(root.body.body, tirx.stmt.For) assert isinstance(root.body.body.body, tirx.stmt.SBlockRealize) outer_block = root.body.body.body.block assert len(outer_block.match_buffers) == 1 buffer_C = outer_block.match_buffers[0].buffer tvm.ir.assert_structural_equal(buffer_C.shape, [T.int32(16), T.int32(1), T.int32(4)]) assert isinstance(outer_block.body, tirx.stmt.For) assert isinstance(outer_block.body.body, tirx.stmt.SBlockRealize) inner_block = outer_block.body.body.block assert len(inner_block.match_buffers) == 1 buffer_D = inner_block.match_buffers[0].buffer tvm.ir.assert_structural_equal(buffer_D.shape, [T.int32(4), T.int32(1), T.int32(4)]) def block_elements(): @T.prim_func(s_tir=True) def block_elements(a: T.handle, b: T.handle) -> None: A = T.match_buffer(a, (16, 16), "float32") B = T.match_buffer(b, (1, 1), "float32") with T.sblock("update"): vi = T.axis.S(1, 0) T.where(True) T.reads(A[0:16, 0:16]) T.writes(B[0, 0]) T.sblock_attr({"attr_key": "attr_value"}) C = T.sblock_alloc_buffer((4, 4), dtype="float32") D = T.match_buffer(A[0:4, 0], (4, 1)) with T.init(): B[0, 0] = T.float32(0) B[0, 0] = A[0, 0] + B[0, 0] + C[1, 1] + D[2, 0] return block_elements def test_block_elements(): func = block_elements() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) assert isinstance(rt_func.body.block, tirx.stmt.SBlock) assert isinstance(rt_func.body.block.body, tirx.stmt.SBlockRealize) assert isinstance(rt_func.body.block.body.block, tirx.stmt.SBlock) block = rt_func.body.block.body.block assert isinstance(block.body, tirx.stmt.BufferStore) assert isinstance(block.init, tirx.stmt.BufferStore) assert len(block.annotations) == 1 assert block.annotations["attr_key"] == "attr_value" def opaque_block(): @T.prim_func(s_tir=True) def opaque_block(a: T.handle, b: T.handle) -> None: A = T.match_buffer(a, (16, 16), "float32") B = T.match_buffer(b, (16, 16), "float32") for i in range(16): for j in range(16): with T.sblock(): T.reads([]) T.writes(A[i, j]) A[i, j] = T.float32(0) with T.sblock(): T.reads([A[i, 0:16]]) T.writes([B[i, 0:16]]) for j in range(16): B[i, j] = A[i, j] return opaque_block def test_opaque_block(): func = opaque_block() rt_func = tvm.script.from_source(func.script()) tvm.ir.assert_structural_equal(func, rt_func) root_block = rt_func.body.block assert isinstance(root_block, tirx.stmt.SBlock) assert isinstance(root_block.body, tirx.stmt.For) assert isinstance(root_block.body.body[0], tirx.stmt.For) assert isinstance(root_block.body.body[0].body, tirx.stmt.SBlockRealize) assert isinstance(root_block.body.body[0].body.block, tirx.stmt.SBlock) assert len(root_block.body.body[0].body.block.iter_vars) == 0 assert isinstance(root_block.body.body[1], tirx.stmt.SBlockRealize) assert isinstance(root_block.body.body[1].block, tirx.stmt.SBlock) assert len(root_block.body.body[1].block.iter_vars) == 0 def rank0(): @T.prim_func(s_tir=True) def rank0(a: T.handle) -> None: A = T.match_buffer(a, (), "float32") B = T.sblock_alloc_buffer((), "float32") A[()] = 2 B[()] = A[()] return rank0 def rank0_block(): @T.prim_func(s_tir=True) def rank0_block(a: T.handle) -> None: A = T.match_buffer(a, (), "float32") B = T.sblock_alloc_buffer((), "float32") B[()] = A[()] with T.sblock("update"): T.reads([A[()]]) T.writes([B[()]]) for i in range(1): B[()] = A[()] return rank0_block def select(): @T.prim_func(s_tir=True) def select(a: T.handle) -> None: A = T.match_buffer(a, (), "float32") A[()] = T.Select(True, 1, 2) return select def minmax(): @T.prim_func(s_tir=True) def minmax(a: T.handle) -> None: A = T.match_buffer(a, (), "float32") A[()] = T.min(1, 2) A[()] = T.max(1, 2) return minmax def abs(): @T.prim_func(s_tir=True) def abs(a: T.handle) -> None: A = T.match_buffer(a, (128, 128), "float32") for i, j in T.grid(128, 128): with T.sblock("A"): vi, vj = T.axis.remap("SS", [i, j]) A[vi, vj] = T.abs(A[vi, vj]) return abs def constant_folding(): @T.prim_func(s_tir=True) def constant_folding(a: T.handle) -> None: A = T.match_buffer(a, (), "float32") A[()] = T.min(2.2, 5.2) A[()] = T.max(T.float32(2.2), T.float32(T.float32(5.2))) A[()] = T.min(2.2, 5.0) return constant_folding def simplify_bracket(): # uninitialized variables @T.prim_func(check_well_formed=False, s_tir=True) def simplify_bracket() -> None: a = T.int32() b = T.int32() c = T.int32() d = T.int32() T.evaluate(a + b * (c + d)) return simplify_bracket def var_with_same_name(): @T.prim_func(s_tir=True) def var_with_same_name(a: T.handle) -> None: A = T.match_buffer(a, (16, 16), "float32") for i, j in T.grid(16, 16): with T.sblock(): vi, vj = T.axis.remap("SS", [i, j]) A[vi, vj] = 0 for i, j in T.grid(16, 16): with T.sblock(): vi, vj = T.axis.remap("SS", [i, j]) A[vi, vj] = 0 return var_with_same_name def test_same_name_var(): func = var_with_same_name() out_str = func.script() rt_func = tvm.script.from_source(out_str) tvm.ir.assert_structural_equal(func, rt_func) assert out_str.count("for i, j in T.grid(16, 16)") == 2 assert out_str.find("i_") == -1 assert out_str.find("i_") == -1 def while_loop(): @T.prim_func(s_tir=True) def while_loop(a: T.handle, b: T.handle) -> None: A = T.match_buffer(a, (16,), "float32") B = T.match_buffer(b, (16,), "float32") i = T.sblock_alloc_buffer((), "int32", scope="local") for ii in range(16): with T.sblock(): vi = T.axis.S(16, ii) B[vi] = 0 while i[()] < 10: for j in range(16): B[j] += A[j] return while_loop # fmt: off def primfunc_with_allocate_annotations(): @T.prim_func(s_tir=True) def primfunc_with_allocate_annotations(placeholder_28: T.handle, T_cast_6: T.handle) -> None: # function attr dict T.func_attr({"global_symbol": "tvmgen_default_fused_nn_max_pool2d_cast", "tirx.noalias": True}) placeholder_29 = T.match_buffer(placeholder_28, [802816], dtype="uint8", elem_offset=0, align=64, offset_factor=1) T_cast_7 = T.match_buffer(T_cast_6, [200704], dtype="int16", elem_offset=0, align=64, offset_factor=1) # body tensor_2 = T.alloc_buffer((200704,), "uint8", annotations={"attr1_key": "attr1_value"}) for ax0_ax1_fused_4 in T.serial(0, 56): for ax2_4 in T.serial(0, 56): for ax3_init in T.serial(0, 64): tensor_2[(((ax0_ax1_fused_4*3584) + (ax2_4*64)) + ax3_init)] = T.uint8(0) for rv0_rv1_fused_1, ax3_2 in T.grid(9, 64): tensor_2[(((ax0_ax1_fused_4*3584) + (ax2_4*64)) + ax3_2)] = T.max(tensor_2[(((ax0_ax1_fused_4*3584) + (ax2_4*64)) + ax3_2)], T.if_then_else(((((ax0_ax1_fused_4*2) + T.floordiv(rv0_rv1_fused_1, 3)) < 112) and (((ax2_4*2) + T.floormod(rv0_rv1_fused_1, 3)) < 112)), placeholder_29[(((((ax0_ax1_fused_4*14336) + (T.floordiv(rv0_rv1_fused_1, 3)*7168)) + (ax2_4*128)) + (T.floormod(rv0_rv1_fused_1, 3)*64)) + ax3_2)], T.uint8(0), dtype="uint8")) for ax0_ax1_fused_5 in T.serial(0, 56): for ax2_5, ax3_3 in T.grid(56, 64): T_cast_7[(((ax0_ax1_fused_5*3584) + (ax2_5*64)) + ax3_3)] = T.cast(tensor_2[(((ax0_ax1_fused_5*3584) + (ax2_5*64)) + ax3_3)], "int16") return primfunc_with_allocate_annotations # fmt: on # fmt: off def comm_reducer_single_reduce_group(): @T.prim_func(s_tir=True) def comm_reducer_single_reduce_group(a: T.handle, b: T.handle) -> None: T.func_attr({"global_symbol": "main", "tirx.noalias": True}) threadIdx_x = T.env_thread("threadIdx.x") A = T.match_buffer(a, [16384], dtype="float32") for i in T.serial(0, 128): T.launch_thread(threadIdx_x, 128) reduce_temp0 = T.alloc_buffer((1,), scope="local") with T.attr(T.comm_reducer(lambda x, y: x + y, [T.float32(0)]), "reduce_scope", T.int32(0)): T.evaluate(T.tvm_thread_allreduce(T.uint32(1), A[i * 128 + threadIdx_x], True, reduce_temp0.data, threadIdx_x, dtype="handle")) return comm_reducer_single_reduce_group def comm_reducer_multiple_reduce_groups(): @T.prim_func(s_tir=True) def comm_reducer_multiple_reduce_groups(a: T.handle, b: T.handle) -> None: T.func_attr({"global_symbol": "main", "tirx.noalias": True}) threadIdx_x = T.env_thread("threadIdx.x") A = T.match_buffer(a, [16384], dtype="float32") for i in T.serial(0, 128): T.launch_thread(threadIdx_x, 128) reduce_temp0 = T.alloc_buffer((1,), scope="local") with T.attr(T.comm_reducer(lambda x0, x1, y0, y1: (T.Select((x1 >= y1), x0, y0), T.Select((x1 >= y1), x1, y1)), [T.int32(-1), T.min_value("float32")]), "reduce_scope", T.int32(0)): T.evaluate(T.tvm_thread_allreduce(T.uint32(1), A[i * 128 + threadIdx_x], True, reduce_temp0.data, threadIdx_x, dtype="handle")) return comm_reducer_multiple_reduce_groups def multiple_commreducer(): # normal_reduce_temp0 is treated as uninitialized value @T.prim_func(check_well_formed=False, s_tir=True) def multiple_commreducer() -> None: normal_reduce_temp0 = T.Buffer([1], dtype="float32", strides=[1], scope="local") normal_reduce_temp1 = T.Buffer([1], dtype="float32", strides=[1], scope="local") reduce_temp0 = T.Buffer([1], dtype="float32", strides=[1], scope="local") reduce_temp1 = T.Buffer([1], dtype="float32", strides=[1], scope="local") for ax0_1 in T.thread_binding(0, 32, thread="threadIdx.x"): with T.sblock("T_softmax_maxelem_cross_thread_reduction"): T.attr(T.comm_reducer(lambda x, y: T.max(x, y), [T.min_value("float32")]), "reduce_scope", T.int32(0)) T.evaluate(T.tvm_thread_allreduce(T.uint32(1), normal_reduce_temp0[0], True, reduce_temp0.data, ax0_1, dtype="handle")) for ax0_1 in T.thread_binding(0, 32, thread="threadIdx.x"): with T.sblock("T_softmax_expsum_cross_thread_reduction"): T.attr(T.comm_reducer(lambda x, y: x + y, [T.float32(0)]), "reduce_scope", T.int32(0)) T.evaluate(T.tvm_thread_allreduce(T.uint32(1), normal_reduce_temp1[0], True, reduce_temp1.data, ax0_1, dtype="handle")) return multiple_commreducer # fmt: on def func_div_mod(): # not well-formed: free variables @T.prim_func(check_well_formed=False, s_tir=True) def func_div_mod(): a = T.int32() b = T.int32() T.evaluate(a // b) T.evaluate(a % b) T.evaluate(T.truncmod(a, b)) return func_div_mod def test_div_mod(): func = func_div_mod() rt_func = tvm.script.from_source(func.script(), check_well_formed=False) tvm.ir.assert_structural_equal(func, rt_func, True) assert isinstance(func.body[0].value, tvm.tirx.FloorDiv) assert isinstance(func.body[1].value, tvm.tirx.FloorMod) assert isinstance(func.body[2].value, tvm.tirx.Mod) def loop_extent_dependent(): @T.prim_func(s_tir=True) def loop_extent_dependent(a: T.handle) -> None: A = T.match_buffer(a, [], dtype="int32") for i in T.serial(0, 128): for j in T.serial(0, i): A[()] = A[()] + j return loop_extent_dependent def nontrivial_range_axis(): @T.prim_func(s_tir=True) def nontrivial_range_axis(a: T.handle) -> None: A = T.match_buffer(a, (10), "float32") for i in range(10): with T.sblock("block"): vi = T.axis.spatial((1, 11), i + 1) A[vi - 1] = A[vi - 1] + 1.0 return nontrivial_range_axis def func_with_target_spec_by_config(): @T.prim_func(s_tir=True) def func_with_target_spec_by_config() -> None: T.func_attr( { "kTarget": T.target( { "max_num_threads": 1024, "arch": "sm_70", "thread_warp_size": 32, "kind": "cuda", "tag": "", "keys": ["cuda", "gpu"], "host": T.target({"kind": "llvm", "tag": "", "keys": ["cpu"]}), } ) } ) T.evaluate(0) return func_with_target_spec_by_config def func_with_target_spec_by_str(): @T.prim_func(s_tir=True) def func_with_target_spec_by_str() -> None: T.func_attr({"kTarget": T.target("nvidia/nvidia-a100")}) T.evaluate(0) return func_with_target_spec_by_str def func_with_target_and_host_spec_by_str(): @T.prim_func(s_tir=True) def func(): T.func_attr({"target": T.target("nvidia/nvidia-a100", host="llvm")}) T.evaluate(0) return func def func_root_attr(): @T.prim_func(s_tir=True) def func_root_attr(): with T.sblock("root"): T.sblock_attr({"a": "0"}) T.evaluate(0) return func_root_attr def func_trivial_root_block(): @T.prim_func(s_tir=True) def func(A: T.Buffer(1, "int32")): with T.sblock("root"): A[0] = 0 return func def func_nested_root_block(): @T.prim_func(s_tir=True) def func(A: T.Buffer(1, "int32")): with T.sblock("root"): with T.sblock("block"): A[0] = 0 return func def func_T_ptr_let_statement(): @T.prim_func(s_tir=True) def func_T_ptr_let_statement( args: T.handle, arg_type_ids_handle: T.handle("int32"), num_args: T.int32 ) -> None: # The T.Ptr declaration in the parameter list should parse # correctly, and should be usable as the data pointer in a buffer. arg_type_ids = T.decl_buffer([2], dtype="int32", data=arg_type_ids_handle) arg0: T.let[T.handle] = T.tvm_struct_get(args, 0, 12, dtype="handle") arg1: T.let[T.handle] = T.tvm_struct_get(args, 1, 12, dtype="handle") # The ABI field is an opaque pointer. Retag it explicitly before # binding it to the buffer's exact element pointer type. A_data: T.let[T.handle("float32")] = T.reinterpret( T.handle("float32").ty, T.tvm_struct_get(arg0, 0, 1, dtype="handle"), ) # The buffer declaration has a data pointer defined earlier in # this function. It should only be defined after the data pointer # has been defined, and should not be hoisted into the header of # the function as other buffer_decl statements can be. A = T.decl_buffer([1024], dtype="float32", data=A_data) B_data: T.let[T.handle("float32")] = T.reinterpret( T.handle("float32").ty, T.tvm_struct_get(arg1, 0, 1, dtype="handle"), ) B = T.decl_buffer([1024], dtype="float32", data=B_data) B[0] = A[0] return func_T_ptr_let_statement def func_T_ptr_allocate(): @T.prim_func(s_tir=True) def func_T_ptr_allocate() -> None: A = T.alloc_buffer((1024,)) A[0] = 0.0 return func_T_ptr_allocate def llvm_intrin_call(): @T.prim_func(s_tir=True) def ctpop(A: T.Buffer((16,), "uint8"), B: T.Buffer((16,), "uint8")) -> None: for i in range(0, 16): with T.sblock("A"): vi = T.axis.remap( "S", [ i, ], ) B[vi] = T.call_llvm_pure_intrin( T.llvm_lookup_intrinsic_id("llvm.ctpop.i8"), A[vi], dtype="uint8", ) return ctpop def parse_bufferslice_as_range_bound(): # apparently the use of i in the "outer" block when it is defined outside of a block is wrong @T.prim_func(check_well_formed=False, s_tir=True) def segment_sum( A_ptr: T.handle, B_ptr: T.handle, indptr_ptr: T.handle, n: T.int32, m: T.int32 ) -> None: A = T.match_buffer(A_ptr, [m], dtype="float32") B = T.match_buffer(B_ptr, [n], dtype="float32") indptr = T.match_buffer(indptr_ptr, [n + 1], dtype="int32") for i in T.serial(n): with T.sblock("outer"): vi = T.axis.spatial(n, i) T.reads(indptr[i : i + 2], B[vi], A[indptr[i] : indptr[i + 1]]) T.writes(B[vi]) for j in T.serial(indptr[i], indptr[i + 1]): with T.sblock("inner"): vj = T.axis.reduce(m, j) T.reads(B[vi], A[vj]) T.writes(B[vi]) with T.init(): B[vi] = T.float32(0) B[vi] = B[vi] + A[vj] return segment_sum def int64_support(): @T.prim_func(s_tir=True) def elementwise_shape_int64(a: T.handle, c: T.handle) -> None: A = T.match_buffer(a, (T.int64(128), T.int64(128)), dtype="float32") B = T.sblock_alloc_buffer((T.int64(128), T.int64(128)), dtype="float32") C = T.match_buffer(c, (T.int64(128), T.int64(128)), dtype="float32") for i, j in T.grid(128, 128): with T.sblock("B"): vi, vj = T.axis.remap("SS", [i, j]) B[vi, vj] = A[vi, vj] * 2.0 for i, j in T.grid(T.int64(128), T.int64(128)): with T.sblock("C"): vi, vj = T.axis.remap("SS", [i, j]) C[vi, vj] = B[vi, vj] + 1.0 return elementwise_shape_int64 def string_annotation_escaping(): @T.prim_func(s_tir=True) def string_annotation_of_special_chars(): T.func_attr( { "key1": '"\'hello\t\r"', "key2": """ %1 = add i32 %0, %0 %2 = add i32 %0, %1 %3 = add i32 %1, %2 """, } ) T.evaluate(0) return string_annotation_of_special_chars def pointer_type(): @T.prim_func(s_tir=True) def func_with_ptr_type_annotations(x: T.handle("int32"), y: T.handle("int32", "shared")): xx = T.alloc_buffer((16,), "int32") yy = T.alloc_buffer((16,), "int32", scope="shared") a: T.let[T.handle("int32")] = T.address_of(xx[0], dtype="handle") b: T.let[T.handle("int32", "shared")] = T.address_of(yy[0], dtype="handle") T.evaluate(T.call_extern("copy", a, b, dtype="")) return func_with_ptr_type_annotations def buffer_axis_separator(): @T.prim_func(s_tir=True) def element_wise(a: T.handle, c: T.handle) -> None: A = T.match_buffer(a, (128, 128), "float32", axis_separators=[1]) C = T.match_buffer(c, (128, 128), "float32") B = T.sblock_alloc_buffer((128, 128), "float32", axis_separators=[1]) for i, j in T.grid(128, 128): with T.sblock("B"): vi, vj = T.axis.remap("SS", [i, j]) B[vi, vj] = A[vi, vj] * T.float32(2) for i, j in T.grid(128, 128): with T.sblock("C"): vi, vj = T.axis.remap("SS", [i, j]) C[vi, vj] = B[vi, vj] + T.float32(1) return element_wise def buffer_ramp_access_as_slice_index(): @T.prim_func(s_tir=True) def buffer_ramp_access(a: T.handle, b: T.handle, c: T.handle) -> None: A = T.match_buffer(a, (128,), "float32") B = T.match_buffer(b, (128,), "float32") C = T.match_buffer(c, (128,), "float32") for i in range(128): A[i : i + 1 : 1] = i for i in range(4): B[i * 32 : i * 32 + 32] = A[i * 32 : i * 32 + 32 : 1] + T.broadcast(1.0, 32) for i in range(4): C[i : i + 128 : 4] = B[i : i + 128 : 4] + T.broadcast(1.0, 32) return buffer_ramp_access def ramp_int64(): @T.prim_func(s_tir=True) def func() -> None: T.evaluate(T.Ramp(T.int64(0), 1, 3)) return func def scalable_vectors(): @T.prim_func(s_tir=True) def func(a: T.handle): A = T.match_buffer(a, (200,), "float32") A[T.Ramp(11, 2, 4 * tirx.vscale())] = T.Broadcast(125, 4 * tirx.vscale()) return func def predicated_buffer_load_store(): @T.prim_func(s_tir=True) def func(a: T.handle, b: T.handle): A = T.match_buffer(a, (4,), "float32") B = T.match_buffer(b, (8,), "float32") for i_0 in range(4): load_a = T.meta_var( A.vload([T.Ramp(i_0, 1, 4)], predicate=T.Broadcast(T.bool(True), 4)) ) B.vstore([T.Ramp(0, 2, 4)], load_a, predicate=T.Broadcast(T.bool(True), 4)) return func def let_expression(): @T.prim_func(s_tir=True) def func(): x = T.int32() T.evaluate(T.Let(x + 1, where={x: 1})) return func def test_void_ptr_vs_handle(): """An untyped handle is the canonical void-pointer type.""" # Generates PointerType(PrimType::Void()) @T.prim_func(s_tir=True) def void_ptr(out_ret_value: T.handle("void")): T.evaluate(out_ret_value) # Generates PointerType::VoidPointerTy() @T.prim_func(s_tir=True) def handle(out_ret_value: T.handle): T.evaluate(out_ret_value) tvm.ir.assert_structural_equal(void_ptr.params[0].ty, handle.params[0].ty) script = void_ptr.script() assert "out_ret_value: T.handle" in script assert 'T.handle("void")' not in script tvm.ir.assert_structural_equal(void_ptr, tvm.script.from_source(script)) @T.prim_func(s_tir=True) def scoped_void_ptr(out_ret_value: T.handle("void", "shared")): T.evaluate(out_ret_value) scoped_script = scoped_void_ptr.script() assert 'out_ret_value: T.handle(storage_scope="shared")' in scoped_script assert 'T.handle("void"' not in scoped_script tvm.ir.assert_structural_equal(scoped_void_ptr, tvm.script.from_source(scoped_script)) def void_ptr(): @T.prim_func(s_tir=True) def func(out_ret_value: T.handle("void")): T.evaluate(out_ret_value) return func def decl_buffer(): @T.prim_func(s_tir=True) def func(A: T.Buffer((16, 16), "float32"), B: T.Buffer((16, 16), "float32")) -> None: A_flattened = T.decl_buffer(data=A.data, shape=(256,), dtype="float32") B_flattened = T.decl_buffer(data=B.data, shape=(256,), dtype="float32") C_alias = T.decl_buffer(data=A_flattened.data, shape=(256,), dtype="float32") for i in range(256): B_flattened[i] = A_flattened[i] + C_alias[i] + T.float32(1.0) return func def allocate_and_decl_buffer(): @T.prim_func(s_tir=True) def func(A: T.Buffer((16,), "float32"), B: T.Buffer((16,), "float32")) -> None: D = T.alloc_buffer((16,)) for i in range(4): C = T.alloc_buffer((4,)) for j in range(4): C[j] = A[i * 4 + j] + T.float32(1.0) for j in range(4): D[j] = C[j] for j in range(4): B[i * 4 + j] = D[j] return func def alloc_buffer_example(): @T.prim_func(s_tir=True) def func(a: T.handle, c: T.handle): A = T.match_buffer(a, (128,), "float32") C = T.match_buffer(c, (128,), "float32") B = T.alloc_buffer((128,), "float32") for i in range(128): B[i] = A[i] * T.float32(2) for i in range(128): C[i] = B[i] + T.float32(1) return func def float_infinity(): @T.prim_func(s_tir=True) def func( placeholder: T.Buffer((1, 512, 768), "float32"), T_isinf: T.Buffer((1, 512, 768), "bool") ) -> None: # function attr dict T.func_attr({"global_symbol": "main", "tirx.noalias": True}) # body # with T.sblock("root") for i0, i1, i2 in T.grid(1, 512, 768): with T.sblock("T_isinf"): ax0, ax1, ax2 = T.axis.remap("SSS", [i0, i1, i2]) T.reads(placeholder[ax0, ax1, ax2]) T.writes(T_isinf[ax0, ax1, ax2]) T_isinf[ax0, ax1, ax2] = T.fabs( placeholder[ax0, ax1, ax2], dtype="float32" ) == T.float32("inf") and not (T.isnan(placeholder[ax0, ax1, ax2], dtype="bool")) return func def minimal_i32_literal(): @T.prim_func(s_tir=True) def func() -> None: T.evaluate(T.int32(-2147483648)) T.evaluate(-T.int64(2147483648)) return func def boolean_argument(): @T.prim_func(s_tir=True) def func(a: T.boolean) -> None: T.evaluate(a) return func def bool_argument(): @T.prim_func(s_tir=True) def func(a: T.bool) -> None: T.evaluate(a) return func def bool_variable_annotation(): @T.prim_func(s_tir=True) def func() -> None: a: T.let[T.bool] = T.call_extern("dummy", dtype="bool") T.evaluate(0) return func def return_none(): @T.prim_func(s_tir=True) def func(): T.evaluate(0) return func def bool_primitive(): @T.prim_func(s_tir=True) def func() -> None: T.evaluate(T.bool(True)) return func def bool_cast(): # uninitialized var @T.prim_func(check_well_formed=False, s_tir=True) def func() -> None: a = T.bool() T.evaluate(T.bool(T.int32(0))) T.evaluate(a == T.bool(False)) return func def implicit_evaluate(): @T.prim_func(s_tir=True) def func(A: T.Buffer(1, "int32")): T.evaluate(T.assume(A[0] == 5)) A[0] = 10 return func def if_true_else(): @T.prim_func(s_tir=True) def func() -> None: if True: T.evaluate(0) else: T.evaluate(1) return func def elif_chain_without_else(): @T.prim_func(s_tir=True) def func(i: T.int32) -> None: if i == 0: T.evaluate(0) elif i == 1: T.evaluate(1) elif i == 2: T.evaluate(2) return func def elif_chain_with_else(): @T.prim_func(s_tir=True) def func(i: T.int32) -> None: if i == 0: T.evaluate(0) elif i == 1: T.evaluate(1) elif i == 2: T.evaluate(2) else: T.evaluate(3) return func def nested_boolean_expressions(): expressions = { "and_lhs_and": lambda i, j, k: tirx.all(tirx.all(i, j), k), "and_rhs_and": lambda i, j, k: tirx.all(i, tirx.all(j, k)), "and_lhs_or": lambda i, j, k: tirx.all(tirx.any(i, j), k), "and_rhs_or": lambda i, j, k: tirx.all(i, tirx.any(j, k)), "or_lhs_and": lambda i, j, k: tirx.any(tirx.all(i, j), k), "or_rhs_and": lambda i, j, k: tirx.any(i, tirx.all(j, k)), "or_lhs_or": lambda i, j, k: tirx.any(tirx.any(i, j), k), "or_rhs_or": lambda i, j, k: tirx.any(i, tirx.any(j, k)), "and_of_ors": lambda i, j, k: tirx.all( tirx.any(i, j), tirx.any(j, k), tirx.any(i, k), i, j, k ), "or_of_ands": lambda i, j, k: tirx.any( tirx.all(i, j), tirx.all(j, k), tirx.all(i, k), i, j, k ), } def make_ir_generator(name, expression): def inner(): @T.prim_func(s_tir=True) def func(A: T.Buffer(1, "bool"), i: T.bool, j: T.bool, k: T.bool): A[0] = expression(i, j, k) return func inner.__name__ = f"nested_boolean_expr_{name}" return inner for name, expression in expressions.items(): generator = make_ir_generator(name, expression) yield generator def multi_env_threads(): @T.prim_func(s_tir=True) def func(A: T.Buffer(128, "float32"), C: T.Buffer(128, "float32")): B = T.sblock_alloc_buffer([128], dtype="float32") for i in T.thread_binding(128, thread="threadIdx.x"): B[i] = A[i] + 1.0 for i in T.thread_binding(128, thread="threadIdx.x"): C[i] = B[i] + 2.0 mod = tvm.s_tir.transform.LowerOpaqueBlock()( tvm.IRModule.from_expr(func.with_attr("global_symbol", "main")) ) return mod["main"] def intrinsic_pow(): @T.prim_func(s_tir=True) def func(): T.pow(T.float32(1), T.float32(1)) return func def bind_var(): @T.prim_func(s_tir=True) def func(): x = T.bind(0) y = T.bind(0) T.evaluate(0) T.evaluate(0) return func def string_stride(): @T.prim_func(s_tir=True) def main(a: T.handle, b: T.handle): T.func_attr({"global_symbol": "main", "tirx.noalias": True}) n = T.int32() A = T.match_buffer(a, (n,), strides=("A_s0",), buffer_type="auto") B = T.match_buffer(b, (n,), strides=("B_s0",), buffer_type="auto") blockIdx_x = T.launch_thread("blockIdx.x", (n + 63) // 64) threadIdx_x = T.launch_thread("threadIdx.x", 64) if T.likely(blockIdx_x * 64 + threadIdx_x < n): B2 = T.decl_buffer((B.strides[0] * n,), data=B.data) A2 = T.decl_buffer((A.strides[0] * n,), data=A.data) B2[(blockIdx_x * 64 + threadIdx_x) * B.strides[0]] = A2[ (blockIdx_x * 64 + threadIdx_x) * A.strides[0] ] * T.float32(2) return main def string_stride_int64(): @T.prim_func(s_tir=True) def main(a: T.handle, b: T.handle): T.func_attr({"global_symbol": "main", "tirx.noalias": True}) n = T.int64() A_s0 = T.int64() B_s0 = T.int64() A = T.match_buffer(a, (n,), strides=(A_s0,), buffer_type="auto") B = T.match_buffer(b, (n,), strides=(B_s0,), buffer_type="auto") for i in range(n): B[i] = A[i] return main def merge_shape_var_def(): # uninitialized vars @T.prim_func(check_well_formed=False, s_tir=True) def main(A: T.handle, B: T.handle): # fmt: off T.func_attr({"global_symbol": "main", "tirx.noalias": True}) m, n = T.int32(), T.int32() A_1 = T.match_buffer(A, (m, n), strides=("A_1_s0", "A_1_s1"), buffer_type="auto") B_1 = T.match_buffer(B, (m, n), strides=("B_1_s0", "B_1_s1"), buffer_type="auto") for i_outer, j_outer, i_inner in T.grid((m + 9) // 10, (n + 4) // 5, 10): if T.likely(i_outer * 10 + i_inner < m): for j_inner in range(5): if T.likely(j_outer * 5 + j_inner < n): cse_v2: T.let[T.int32] = j_outer * 5 + j_inner cse_v1: T.let[T.int32] = i_outer * 10 + i_inner B_2 = T.decl_buffer( (B_1.strides[0] * m,), data=B_1.data, strides=("B_2_s0",), buffer_type="auto", ) A_2 = T.decl_buffer( (A_1.strides[0] * m,), data=A_1.data, strides=("A_2_s0",), buffer_type="auto", ) B_2[cse_v1 * B_1.strides[0] + cse_v2 * B_1.strides[1]] = A_2[ cse_v1 * A_1.strides[0] + cse_v2 * A_1.strides[1] ] # fmt: on return main def if_then_else_var(): @T.prim_func(s_tir=True) def main(n: T.int32): if n == 0: x = 5 T.evaluate(x) else: x = 10 T.evaluate(x) return main def tvm_shfl_builtins(): @T.prim_func(s_tir=True) def func( A: T.handle("float32"), B: T.handle("float32"), C: T.handle("float32"), ): blockIdx_x = T.launch_thread("blockIdx.x", 1) threadIdx_x = T.launch_thread("threadIdx.x", 32) A_warp = T.alloc_buffer((1,), scope="local") B_warp = T.alloc_buffer((1,), scope="local") red_buf0 = T.alloc_buffer((1,), scope="local") A_warp_1 = T.decl_buffer((32,), data=A_warp.data, scope="local") A_1 = T.decl_buffer((32,), data=A) # A is a handle param A_warp_1[0] = A_1[threadIdx_x] B_warp_1 = T.decl_buffer((32,), data=B_warp.data, scope="local") T.tvm_storage_sync("warp") B_warp_1[0] = T.tvm_warp_shuffle( T.tvm_warp_activemask(), A_warp_1[0], threadIdx_x % 4 * 8 + threadIdx_x // 4, 32, 32 ) + T.float32(1) red_buf0_1 = T.decl_buffer((1,), data=red_buf0.data, scope="local") with T.attr( T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]), "reduce_scope", T.int32(0), ): mask = T.alloc_buffer((1,), "uint32", scope="local") t0 = T.alloc_buffer((1,), scope="local") red_buf0_1[0] = A_warp_1[0] mask_1 = T.decl_buffer((1,), "uint32", data=mask.data, scope="local") mask_1[0] = T.tvm_warp_activemask() t0_1 = T.decl_buffer((1,), data=t0.data, scope="local") t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 16, 32, 32) red_buf0_1[0] = red_buf0_1[0] + t0_1[0] t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 8, 32, 32) red_buf0_1[0] = red_buf0_1[0] + t0_1[0] t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 4, 32, 32) red_buf0_1[0] = red_buf0_1[0] + t0_1[0] t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 2, 32, 32) red_buf0_1[0] = red_buf0_1[0] + t0_1[0] t0_1[0] = T.tvm_warp_shuffle_down(mask_1[0], red_buf0_1[0], 1, 32, 32) red_buf0_1[0] = red_buf0_1[0] + t0_1[0] red_buf0_1[0] = T.tvm_warp_shuffle(mask_1[0], red_buf0_1[0], 0, 32, 32) # NOTE(Zihao): test tvm_warp_shuffle_up red_buf0_1[0] = T.tvm_warp_shuffle_up(mask_1[0], red_buf0_1[0], 0, 32, 32) if threadIdx_x == 0: C_1 = T.decl_buffer((1,), data=C) C_1[0] = red_buf0_1[0] B_1 = T.decl_buffer((32,), data=B) B_1[threadIdx_x] = B_warp_1[0] return func def make_packed_api_result(): @T.prim_func(s_tir=True) def func(A: T.Buffer(64, "float32")): T.func_attr({"global_symbol": "main", "target": T.target("cuda")}) bx = T.launch_thread("blockIdx.x", 64) T.evaluate(A[bx]) mod = tvm.IRModule.from_expr(func) return tvm.tirx.transform.MakePackedAPI()(mod) def tvm_struct_set_generated_in_cpp(): """Ensure same dtype for tvm_struct_set in Python/C++ The TVMStructSet method in C++, used internally by LowerTVMBuiltin, and the Python method `T.tvm_struct_set`, used when parsing TVMScript should use the same dtype "int32". """ @I.ir_module(s_tir=True) class Module: @T.prim_func(s_tir=True) def tir_packed_call(A: T.Buffer(16)): T.attr(0, "device_id", 0) T.attr(0, "device_type", 0) T.evaluate( T.tvm_call_cpacked( "tvm_test_cpacked", T.tvm_stack_make_array( A.data, T.tvm_stack_make_shape(16, dtype="handle"), T.reinterpret(T.uint64(0), dtype="handle"), T.uint32(1), T.Cast("float32", 0), 0, dtype="handle", ), dtype="int32", ) ) return tvm.tirx.transform.LowerTVMBuiltin()(Module) def ir_module_with_attrs(): @I.ir_module(s_tir=True) class Module: I.module_attrs({"attr": 10}) @T.prim_func(s_tir=True) def tir_func(A: T.Buffer(16, "int32"), B: T.Buffer(16, "int32")): for i in range(16): B[i] = A[i] return Module def nested_seqstmt(): """Nested SeqStmt should be normalized to flat SeqStmt Nested SeqStmt are representable in the TIR structures, but are flattened when converted to TVMScript. Previously, this could cause failures to round-trip through TVMScript, including erroneous use of TVMScript's concise-scoping rules. This was resolved by normalizing nested SeqStmt in TIR, such that the use of `tirx.SeqStmt` below results in a single flat `tirx.SeqStmt` containing the three `tirx.Evaluate` calls. """ func = tvm.tirx.PrimFunc( params=[], body=tvm.tirx.SeqStmt( [ tvm.tirx.SeqStmt([tvm.tirx.Evaluate(0), tvm.tirx.Evaluate(1)]), tvm.tirx.Evaluate(2), ] ), ) return func def subroutine_call(): """A GlobalVar may reference other functions in the module""" @I.ir_module(s_tir=True) class mod: @T.prim_func(s_tir=True) def main(A: T.Buffer(16, "float32")): mod.subroutine(A.data, T.int32(16)) @T.prim_func(s_tir=True) def subroutine(A_data: T.handle("float32"), n: T.int32): T.evaluate(0) return mod def subroutine_call_returning_int(): """An internal function call may return non-void""" @I.ir_module(s_tir=True) class mod: @T.prim_func(s_tir=True) def main(A: T.Buffer(2, "float32")): mod.subroutine(A[0]) + mod.subroutine(A[1]) @T.prim_func(s_tir=True) def subroutine(x: T.float32) -> T.float32: T.ret(x * x) return mod def undefined_data_ptr_in_decl_buffer(): """The T.decl_buffer syntax should not introduce an Allocate While T.decl_buffer can be used to represent an Allocate/DeclBuffer pair, performing a round-trip through TVMScript should not introduce an Allocate node. """ # uninitialized var @T.prim_func(check_well_formed=False, s_tir=True) def func(): data_ptr = T.handle("float32") buf = T.decl_buffer(shape=[1], dtype="float32", data=data_ptr) T.evaluate(buf[0]) return func def undefined_shape_in_decl_buffer(): # uninitialized var @T.prim_func(check_well_formed=False, s_tir=True) def func(): size = T.int32() buf = T.decl_buffer(shape=[size], dtype="float32") T.evaluate(buf[0]) return func def undefined_stride_in_decl_buffer(): # uninitialized var @T.prim_func(check_well_formed=False, s_tir=True) def func(): stride = T.int32() data_ptr = T.handle("float32") buf = T.decl_buffer(shape=[1], dtype="float32", data=data_ptr, strides=[stride]) T.evaluate(buf[0]) return func def undefined_elem_offset_in_decl_buffer(): # uninitialized var @T.prim_func(check_well_formed=False, s_tir=True) def func(): elem_offset = T.int32() data_ptr = T.handle("float32") buf = T.decl_buffer(shape=[1], dtype="float32", data=data_ptr, elem_offset=elem_offset) T.evaluate(buf[0]) return func def subroutine_call_without_arguments(): @I.ir_module(s_tir=True) class mod: @T.prim_func(s_tir=True) def main(): # Should be equivalent to the bare "mod.subroutine()", but # that relies on `GlobalVar.__call__` returning the # correct IR type. tirx.call_tir(mod.subroutine) @T.prim_func(s_tir=True) def subroutine(): T.evaluate(0) return mod def return_zero(): @T.prim_func(s_tir=True) def func() -> T.int32: T.ret(0) return func def return_zero_private(): @T.prim_func(private=True, s_tir=True) def func() -> T.int32: T.ret(0) return func def return_zero_private_with_attr(): @T.prim_func(private=True, s_tir=True) def func() -> T.int32: T.func_attr({"greeting": "hello"}) T.ret(0) return func def func_attr_with_list(): @T.prim_func(s_tir=True) def func( A: T.Buffer((128, 128), "float32"), B: T.Buffer((128, 128), "float32"), D: T.Buffer((128, 128), "float32"), ) -> None: T.func_attr({"global_symbol": "main", "tirx.noalias": True, "layout_free_buffers": [1]}) C = T.sblock_alloc_buffer([128, 128], dtype="float32") for i0, i1, i2 in T.grid(128, 128, 128): with T.sblock("C"): x, y, k = T.axis.remap("SSR", [i0, i1, i2]) with T.init(): C[x, y] = T.float32(0) C[x, y] = C[x, y] + A[x, k] * B[y, k] for i0, i1 in T.grid(128, 128): with T.sblock("D"): T.sblock_attr({"layout_free_placeholders": [C]}) x, y = T.axis.remap("SS", [i0, i1]) D[x, y] = C[x, y] + T.float32(1) return func def func_with_loop_jumps(): @T.prim_func(s_tir=True) def func(In: T.Buffer((1,), "int32"), Out: T.Buffer((2,), "int32")): Out[0] = 0 Out[1] = 0 for i in range(1000): if i % 13 == 0: Out[1] = Out[1] + 1 continue Out[0] = Out[0] + 1 if Out[0] >= In[0]: break return func def func_with_loop_steps(): @T.prim_func(s_tir=True) def func( A: T.Buffer((1024,)), B: T.Buffer((1024,)), C: T.Buffer((1024,)), tid: T.int32, v: T.int32 ): for i in T.serial(tid, 1024, step=2): C[i] = A[i] + B[i] for i in T.unroll(tid, 1024, step=3): C[i] = A[i] + B[i] for i in T.vectorized(tid, 1024, step=4): C[i] = A[i] + B[i] for i in T.parallel(tid, 1024, step=5): C[i] = A[i] + B[i] for i in range(tid, 1024, 6): C[i] = A[i] + B[i] return func def op_of_literal(): op_list = [ (T.exp, 0), (T.exp2, 0), (T.exp10, 0), (T.erf, 0.0), (T.tanh, 0.0), (T.sigmoid, 0.0), (T.log, 0.0), (T.log2, 0.0), (T.log1p, 0.0), (T.tan, 0.0), (T.cos, 0.0), (T.acos, 0.0), (T.acosh, 0.0), (T.sin, 0.0), (T.sinh, 0.0), (T.asin, 0.0), (T.asinh, 0.0), (T.atan, 0.0), (T.atanh, 0.0), (T.atan2, (1.0, 0.0)), (T.sqrt, 0.0), (T.rsqrt, 1.0), (T.nextafter, (0.0, 1.0)), (T.hypot, (1.0, 1.0)), (T.copysign, (1.0, 1.0)), (T.popcount, 0), (T.fmod, (1.0, 1.0)), ] def make_ir_generator(op, arg): def inner(): call_expr = op(*arg) if isinstance(arg, tuple) else op(arg) @T.prim_func(s_tir=True) def func(): T.evaluate(call_expr) return func inner.__name__ = f"{op.__name__}_of_literal" return inner for op, arg in op_list: yield make_ir_generator(op, arg) def relax_extern_func(): @R.function def func(A: R.Tensor([10, 20], "float32")): func = R.ExternFunc("dummy_func") B: R.Tensor([10, 20], "float32") = R.call_dps_packed( func, [A], out_ty=R.Tensor([10, 20], "float32") ) C: R.Tensor(ndim=2, dtype="float32") = R.call_dps_packed( func, [B], out_ty=R.Tensor([10, 20], "float32") ) return C return func def relax_match_cast_ty_proxy(): """TypeProxy subclasses may be used as expressions This is a regression test. The TVMScript parser allows Type to be specified using a default-constructible class (e.g. `R.Tensor` or `R.Shape`) rather than an instance of that class (e.g. `R.Tensor()` or `R.Shape()`). In previous implementations, this was only handled when the `Type` was used in an annotation context. However, a `Type` may also appear as an argument, which is passed to `R.match_cast`. Use of a default-constructible class must be handled in this context as well. """ def make_ir_generator(proxy_subclass): def inner(): @R.function def func(A: R.Any): B = R.match_cast(A, proxy_subclass) return B return func inner.__name__ = subclass.__name__ return inner # Not all subclasses of TypeProxy are default-constructible. # This list is a subset of `TypeProxy.__subclasses__()`, # excluding `PrimProxy` and `DTensorProxy`. subclasses = [ tvm.script.parser.relax.entry.AnyProxy, tvm.script.parser.relax.entry.TensorProxy, tvm.script.parser.relax.entry.CallableProxy, tvm.script.parser.relax.entry.TupleProxy, tvm.script.parser.relax.entry.ShapeProxy, ] for subclass in subclasses: yield make_ir_generator(subclass) def relax_symbolic_var(): """Relax tensors may use symbolic variables.""" N = tvm.tirx.Var("N", "int64") @R.function def func(A: R.Tensor([N], "float16")): B: R.Tensor([N], "float16") = A return B return func def relax_float_symbolic_var(): """Relax scalar variables may use any dtype.""" @R.function def func(value: R.Prim("float16")): return value return func ir_generator = tvm.testing.parameter( launch_env_thread, opt_gemm_lower, opt_conv_tensorcore_lower, opt_conv_tensorcore_mod_host, vthread_func, matmul, rank0, rank0_block, select, minmax, abs, constant_folding, simplify_bracket, while_loop, primfunc_with_allocate_annotations, comm_reducer_single_reduce_group, comm_reducer_multiple_reduce_groups, multiple_commreducer, loop_extent_dependent, nontrivial_range_axis, func_with_target_spec_by_config, func_with_target_spec_by_str, func_with_target_and_host_spec_by_str, func_root_attr, func_trivial_root_block, func_nested_root_block, func_T_ptr_let_statement, func_T_ptr_allocate, llvm_intrin_call, parse_bufferslice_as_range_bound, int64_support, string_annotation_escaping, pointer_type, buffer_axis_separator, buffer_ramp_access_as_slice_index, ramp_int64, scalable_vectors, predicated_buffer_load_store, let_expression, void_ptr, decl_buffer, allocate_and_decl_buffer, alloc_buffer_example, float_infinity, minimal_i32_literal, boolean_argument, bool_argument, bool_variable_annotation, bool_primitive, bool_cast, return_none, implicit_evaluate, if_true_else, elif_chain_without_else, elif_chain_with_else, *nested_boolean_expressions(), multi_env_threads, intrinsic_pow, bind_var, string_stride, string_stride_int64, merge_shape_var_def, if_then_else_var, tvm_shfl_builtins, make_packed_api_result, tvm_struct_set_generated_in_cpp, ir_module_with_attrs, nested_seqstmt, subroutine_call, subroutine_call_returning_int, undefined_data_ptr_in_decl_buffer, undefined_shape_in_decl_buffer, undefined_stride_in_decl_buffer, undefined_elem_offset_in_decl_buffer, subroutine_call_without_arguments, return_zero, return_zero_private, return_zero_private_with_attr, func_attr_with_list, func_with_loop_jumps, func_with_loop_steps, *op_of_literal(), *relax_match_cast_ty_proxy(), relax_symbolic_var, relax_float_symbolic_var, ) relax_ir_generator = tvm.testing.parameter( relax_extern_func, ) show_all_relax_ty = tvm.testing.parameter( by_dict={ "show_all_ty": True, "hide_inferable_ty": False, } ) _NOT_ROUNDTRIP_STABLE: set[str] = set() def test_roundtrip(ir_generator): if getattr(ir_generator, "__name__", "") in _NOT_ROUNDTRIP_STABLE: import pytest pytest.skip(f"{ir_generator.__name__}: not round-trip stable here") original = ir_generator() after_roundtrip = tvm.script.from_source( original.script(show_meta=True), check_well_formed=False ) tvm.ir.assert_structural_equal(original, after_roundtrip, True) def test_relax_roundtrip(relax_ir_generator, show_all_relax_ty): original = relax_ir_generator() after_roundtrip = tvm.script.from_source( original.script( show_meta=True, show_all_ty=show_all_relax_ty, ) ) tvm.ir.assert_structural_equal(original, after_roundtrip, True) def test_return_none_no_trailing_type(): func = return_none() script = func.script() assert "-> None" not in script def test_address_of_buffer(): @T.prim_func(s_tir=True) def func(a: T.handle): A = T.match_buffer(a, (128, 128), "float32") T.evaluate(T.address_of(A)) assert "T.address_of(A[0, 0])" in func.script() def test_assert_stmt_roundtrip_runtime_error(): """RuntimeError assert roundtrips through print->parse.""" @T.prim_func(s_tir=True) def func(x: T.int32): assert x > 0, ("RuntimeError", ["x must be positive"]) script = func.script(show_meta=True) roundtrip = tvm.script.from_source(script, check_well_formed=False) tvm.ir.assert_structural_equal(func, roundtrip, map_free_vars=True) def test_assert_stmt_roundtrip_value_error(): """ValueError assert roundtrips through print->parse.""" @T.prim_func(s_tir=True) def func(x: T.int32): assert x > 0, ("ValueError", ["Shape mismatch"]) script = func.script(show_meta=True) roundtrip = tvm.script.from_source(script, check_well_formed=False) tvm.ir.assert_structural_equal(func, roundtrip, map_free_vars=True) def test_assert_stmt_roundtrip_type_error(): """TypeError assert roundtrips through print->parse.""" @T.prim_func(s_tir=True) def func(x: T.int32): assert x > 0, ("TypeError", ["Expected Tensor but got int"]) script = func.script(show_meta=True) roundtrip = tvm.script.from_source(script, check_well_formed=False) tvm.ir.assert_structural_equal(func, roundtrip, map_free_vars=True) def test_assert_stmt_roundtrip_multi_parts(): """Multi-part message assert roundtrips with structural equality.""" @T.prim_func(s_tir=True) def func(x: T.int32): assert x > 0, ("TypeError", ["Expected ", "Tensor", " but got ", "int"]) script = func.script(show_meta=True) roundtrip = tvm.script.from_source(script, check_well_formed=False) tvm.ir.assert_structural_equal(func, roundtrip, map_free_vars=True) if __name__ == "__main__": tvm.testing.main()