# 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-docstring # ruff: noqa: E501 import tvm.testing from tvm.ir import IRModule, assert_structural_equal from tvm.s_tir import dlight as dl from tvm.script import ir as I from tvm.script import tirx as T from tvm.target import Target def _check(mod_before: IRModule, mod_after: IRModule): target = Target("nvidia/geforce-rtx-3090-ti") with target: mod = dl.ApplyDefaultSchedule( # pylint: disable=not-callable dl.gpu.RMSNorm(), )(mod_before) assert_structural_equal(mod, mod_after) def test_rms_norm_with_casting(): # fmt: off @I.ir_module(s_tir=True) class Before: @T.prim_func(s_tir=True) def main(var_data: T.handle, weight: T.Buffer((4096,), "float16"), var_T_cast: T.handle): T.func_attr({"tirx.noalias": True}) n = T.int32() data = T.match_buffer(var_data, (1, n, 4096), "float16") T_cast = T.match_buffer(var_T_cast, (1, n, 4096), "float16") # with T.sblock("root"): T_cast_1 = T.sblock_alloc_buffer((1, n, 4096)) T_multiply = T.sblock_alloc_buffer((1, n, 4096)) T_multiply_red = T.sblock_alloc_buffer((1, n)) rsqrt = T.sblock_alloc_buffer((1, n)) T_cast_2 = T.sblock_alloc_buffer((4096,)) T_rms_norm = T.sblock_alloc_buffer((1, n, 4096)) for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_cast"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(data[v_ax0, v_ax1, v_ax2]) T.writes(T_cast_1[v_ax0, v_ax1, v_ax2]) T_cast_1[v_ax0, v_ax1, v_ax2] = T.Cast("float32", data[v_ax0, v_ax1, v_ax2]) for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_multiply"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(T_cast_1[v_ax0, v_ax1, v_ax2]) T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) T_multiply[v_ax0, v_ax1, v_ax2] = T_cast_1[v_ax0, v_ax1, v_ax2] * T_cast_1[v_ax0, v_ax1, v_ax2] for ax0, ax1, k2 in T.grid(1, n, 4096): with T.sblock("T_multiply_red"): v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) T.reads(T_multiply[v_ax0, v_ax1, v_k2]) T.writes(T_multiply_red[v_ax0, v_ax1]) with T.init(): T_multiply_red[v_ax0, v_ax1] = T.float32(0) T_multiply_red[v_ax0, v_ax1] = T_multiply_red[v_ax0, v_ax1] + T_multiply[v_ax0, v_ax1, v_k2] for ax0, ax1 in T.grid(1, n): with T.sblock("rsqrt"): v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) T.reads(T_multiply_red[v_ax0, v_ax1]) T.writes(rsqrt[v_ax0, v_ax1]) rsqrt[v_ax0, v_ax1] = T.rsqrt(T_multiply_red[v_ax0, v_ax1] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)) for ax0 in range(4096): with T.sblock("T_cast_1"): v_ax0 = T.axis.spatial(4096, ax0) T.reads(weight[v_ax0]) T.writes(T_cast_2[v_ax0]) T_cast_2[v_ax0] = T.Cast("float32", weight[v_ax0]) for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_rms_norm"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(rsqrt[v_ax0, v_ax1], T_cast_1[v_ax0, v_ax1, v_ax2], T_cast_2[v_ax2]) T.writes(T_rms_norm[v_ax0, v_ax1, v_ax2]) T_rms_norm[v_ax0, v_ax1, v_ax2] = rsqrt[v_ax0, v_ax1] * T_cast_1[v_ax0, v_ax1, v_ax2] * T_cast_2[v_ax2] for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_cast_2"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(T_rms_norm[v_ax0, v_ax1, v_ax2]) T.writes(T_cast[v_ax0, v_ax1, v_ax2]) T_cast[v_ax0, v_ax1, v_ax2] = T.Cast("float16", T_rms_norm[v_ax0, v_ax1, v_ax2]) @I.ir_module(s_tir=True) class After: @T.prim_func(s_tir=True) def main(var_data: T.handle, weight: T.Buffer((4096,), "float16"), var_T_cast: T.handle): T.func_attr({"tirx.is_scheduled": True, "tirx.noalias": True}) n = T.int32() data = T.match_buffer(var_data, (1, n, 4096), "float16") T_cast = T.match_buffer(var_T_cast, (1, n, 4096), "float16") # with T.sblock("root"): T_multiply_local = T.sblock_alloc_buffer((1, n, 4096), scope="local") T_multiply_red_local = T.sblock_alloc_buffer((1, n), scope="local") rsqrt_shared = T.sblock_alloc_buffer((1, n), scope="shared") T_rms_norm_local = T.sblock_alloc_buffer((1, n, 4096), scope="local") data_local = T.sblock_alloc_buffer((1, n, 4096), "float16", scope="local") for ax0_ax1_fused in T.thread_binding(n, thread="blockIdx.x"): for ax2_0 in T.thread_binding(512, thread="threadIdx.x"): for ax2_1 in range(1): for ax2_2 in T.vectorized(8): with T.sblock("data_local"): v0 = T.axis.spatial(1, 0) v1 = T.axis.spatial(n, ax0_ax1_fused) v2 = T.axis.spatial(4096, ax2_0 * 8 + ax2_1 * 8 + ax2_2) T.reads(data[v0, v1, v2]) T.writes(data_local[v0, v1, v2]) data_local[v0, v1, v2] = data[v0, v1, v2] for ax0 in range(8): with T.sblock("T_multiply"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) v_ax2 = T.axis.spatial(4096, ax2_0 * 8 + ax0) T.reads(data_local[v_ax0, v_ax1, v_ax2]) T.writes(T_multiply_local[v_ax0, v_ax1, v_ax2]) T_multiply_local[v_ax0, v_ax1, v_ax2] = T.Cast("float32", data_local[v_ax0, v_ax1, v_ax2]) * T.Cast("float32", data_local[v_ax0, v_ax1, v_ax2]) for ax0 in range(8): with T.sblock("T_multiply_red"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) v_k2 = T.axis.reduce(4096, ax2_0 * 8 + ax0) T.reads(T_multiply_local[v_ax0, v_ax1, v_k2]) T.writes(T_multiply_red_local[v_ax0, v_ax1]) with T.init(): T_multiply_red_local[v_ax0, v_ax1] = T.float32(0) T_multiply_red_local[v_ax0, v_ax1] = T_multiply_red_local[v_ax0, v_ax1] + T_multiply_local[v_ax0, v_ax1, v_k2] with T.sblock("rsqrt"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) T.reads(T_multiply_red_local[v_ax0, v_ax1]) T.writes(rsqrt_shared[v_ax0, v_ax1]) rsqrt_shared[v_ax0, v_ax1] = T.rsqrt(T_multiply_red_local[v_ax0, v_ax1] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)) for ax0_0 in T.thread_binding(512, thread="threadIdx.x"): for ax0_1, ax0_2 in T.grid(1, 8): with T.sblock("T_rms_norm"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) v_ax2 = T.axis.spatial(4096, ax0_0 * 8 + ax0_1 * 8 + ax0_2) T.reads(rsqrt_shared[v_ax0, v_ax1], data_local[v_ax0, v_ax1, v_ax2], weight[v_ax2]) T.writes(T_rms_norm_local[v_ax0, v_ax1, v_ax2]) T_rms_norm_local[v_ax0, v_ax1, v_ax2] = rsqrt_shared[v_ax0, v_ax1] * T.Cast("float32", data_local[v_ax0, v_ax1, v_ax2]) * T.Cast("float32", weight[v_ax2]) for ax0 in T.vectorized(8): with T.sblock("T_cast_local"): v0 = T.axis.spatial(1, 0) v1 = T.axis.spatial(n, ax0_ax1_fused) v2 = T.axis.spatial(4096, ax0_0 * 8 + ax0) T.reads(T_rms_norm_local[v0, v1, v2]) T.writes(T_cast[v0, v1, v2]) T_cast[v0, v1, v2] = T.Cast("float16", T_rms_norm_local[v0, v1, v2]) # fmt: on _check(Before, After) def test_rms_norm_without_casting(): # fmt: off @I.ir_module(s_tir=True) class Before: @T.prim_func(s_tir=True) def main(var_data: T.handle, weight: T.Buffer((4096,), "float32"), var_T_cast: T.handle): T.func_attr({"tirx.noalias": True}) n = T.int32() data = T.match_buffer(var_data, (1, n, 4096)) T_cast = T.match_buffer(var_T_cast, (1, n, 4096)) # with T.sblock("root"): T_multiply = T.sblock_alloc_buffer((1, n, 4096)) T_multiply_red = T.sblock_alloc_buffer((1, n)) rsqrt = T.sblock_alloc_buffer((1, n)) T_rms_norm = T.sblock_alloc_buffer((1, n, 4096)) for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_multiply"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(data[v_ax0, v_ax1, v_ax2]) T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) T_multiply[v_ax0, v_ax1, v_ax2] = data[v_ax0, v_ax1, v_ax2] * data[v_ax0, v_ax1, v_ax2] for ax0, ax1, k2 in T.grid(1, n, 4096): with T.sblock("T_multiply_red"): v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) T.reads(T_multiply[v_ax0, v_ax1, v_k2]) T.writes(T_multiply_red[v_ax0, v_ax1]) with T.init(): T_multiply_red[v_ax0, v_ax1] = T.float32(0) T_multiply_red[v_ax0, v_ax1] = T_multiply_red[v_ax0, v_ax1] + T_multiply[v_ax0, v_ax1, v_k2] for ax0, ax1 in T.grid(1, n): with T.sblock("rsqrt"): v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) T.reads(T_multiply_red[v_ax0, v_ax1]) T.writes(rsqrt[v_ax0, v_ax1]) rsqrt[v_ax0, v_ax1] = T.rsqrt(T_multiply_red[v_ax0, v_ax1] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)) for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_rms_norm"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(rsqrt[v_ax0, v_ax1], data[v_ax0, v_ax1, v_ax2], weight[v_ax2]) T.writes(T_rms_norm[v_ax0, v_ax1, v_ax2]) T_rms_norm[v_ax0, v_ax1, v_ax2] = rsqrt[v_ax0, v_ax1] * data[v_ax0, v_ax1, v_ax2] * weight[v_ax2] for ax0, ax1, ax2 in T.grid(1, n, 4096): with T.sblock("T_cast_2"): v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) T.reads(T_rms_norm[v_ax0, v_ax1, v_ax2]) T.writes(T_cast[v_ax0, v_ax1, v_ax2]) T_cast[v_ax0, v_ax1, v_ax2] = T_rms_norm[v_ax0, v_ax1, v_ax2] @I.ir_module(s_tir=True) class After: @T.prim_func(s_tir=True) def main(var_data: T.handle, weight: T.Buffer((4096,), "float32"), var_T_cast: T.handle): T.func_attr({"tirx.is_scheduled": True, "tirx.noalias": True}) n = T.int32() data = T.match_buffer(var_data, (1, n, 4096)) T_cast = T.match_buffer(var_T_cast, (1, n, 4096)) # with T.sblock("root"): T_multiply_local = T.sblock_alloc_buffer((1, n, 4096), scope="local") T_multiply_red_local = T.sblock_alloc_buffer((1, n), scope="local") rsqrt_shared = T.sblock_alloc_buffer((1, n), scope="shared") T_rms_norm_local = T.sblock_alloc_buffer((1, n, 4096), scope="local") data_local = T.sblock_alloc_buffer((1, n, 4096), scope="local") for ax0_ax1_fused in T.thread_binding(n, thread="blockIdx.x"): for ax2_0 in T.thread_binding(512, thread="threadIdx.x"): for ax2_1 in range(1): for ax2_2 in T.vectorized(8): with T.sblock("data_local"): v0 = T.axis.spatial(1, 0) v1 = T.axis.spatial(n, ax0_ax1_fused) v2 = T.axis.spatial(4096, ax2_0 * 8 + ax2_1 * 8 + ax2_2) T.reads(data[v0, v1, v2]) T.writes(data_local[v0, v1, v2]) data_local[v0, v1, v2] = data[v0, v1, v2] for ax0 in range(8): with T.sblock("T_multiply"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) v_ax2 = T.axis.spatial(4096, ax2_0 * 8 + ax0) T.reads(data_local[v_ax0, v_ax1, v_ax2]) T.writes(T_multiply_local[v_ax0, v_ax1, v_ax2]) T_multiply_local[v_ax0, v_ax1, v_ax2] = data_local[v_ax0, v_ax1, v_ax2] * data_local[v_ax0, v_ax1, v_ax2] for ax0 in range(8): with T.sblock("T_multiply_red"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) v_k2 = T.axis.reduce(4096, ax2_0 * 8 + ax0) T.reads(T_multiply_local[v_ax0, v_ax1, v_k2]) T.writes(T_multiply_red_local[v_ax0, v_ax1]) with T.init(): T_multiply_red_local[v_ax0, v_ax1] = T.float32(0) T_multiply_red_local[v_ax0, v_ax1] = T_multiply_red_local[v_ax0, v_ax1] + T_multiply_local[v_ax0, v_ax1, v_k2] with T.sblock("rsqrt"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) T.reads(T_multiply_red_local[v_ax0, v_ax1]) T.writes(rsqrt_shared[v_ax0, v_ax1]) rsqrt_shared[v_ax0, v_ax1] = T.rsqrt(T_multiply_red_local[v_ax0, v_ax1] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)) for ax0_0 in T.thread_binding(512, thread="threadIdx.x"): for ax0_1, ax0_2 in T.grid(1, 8): with T.sblock("T_rms_norm"): v_ax0 = T.axis.spatial(1, 0) v_ax1 = T.axis.spatial(n, ax0_ax1_fused) v_ax2 = T.axis.spatial(4096, ax0_0 * 8 + ax0_1 * 8 + ax0_2) T.reads(rsqrt_shared[v_ax0, v_ax1], data_local[v_ax0, v_ax1, v_ax2], weight[v_ax2]) T.writes(T_rms_norm_local[v_ax0, v_ax1, v_ax2]) T_rms_norm_local[v_ax0, v_ax1, v_ax2] = rsqrt_shared[v_ax0, v_ax1] * data_local[v_ax0, v_ax1, v_ax2] * weight[v_ax2] for ax0 in T.vectorized(8): with T.sblock("T_cast_local"): v0 = T.axis.spatial(1, 0) v1 = T.axis.spatial(n, ax0_ax1_fused) v2 = T.axis.spatial(4096, ax0_0 * 8 + ax0) T.reads(T_rms_norm_local[v0, v1, v2]) T.writes(T_cast[v0, v1, v2]) T_cast[v0, v1, v2] = T_rms_norm_local[v0, v1, v2] # fmt: on _check(Before, After) if __name__ == "__main__": tvm.testing.main()