# 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: RUF005 """Triton kernel integration with TIR""" from typing import Any import triton from packaging import version from triton.runtime.jit import type_canonicalisation_dict from tvm import tirx from tvm.ir import PointerType, PrimType, is_prim_expr from tvm.runtime import Module from tvm.topi.utils import get_const_int from .external_kernel import BaseKernel if version.parse(triton.__version__) < version.parse("3.3.0"): raise ImportError( f"TIR Triton integration requires Triton >= 3.3.0, but found Triton {triton.__version__}" ) class TritonKernel(BaseKernel): """A kernel from Triton JIT function. This class bridges the Triton kernel with TVM runtime. The compilation includes the following steps: - Deduce the kernel signature and generate the Triton kernel - Embed the compiled kernel into the current IRModule as an external module - Generate a call to the Triton kernel following its calling convention via call_packed. """ def __init__(self, func): self.func = func def compile_to_device_module( self, launch_args: list[int | tirx.Expr], *args: list[Any], **kwargs: dict[str, Any], ) -> tuple[str, Module, list[Any]]: """Compile the kernel to a device module. Parameters ---------- launch_args : List[int] The grid size of the kernel. A list of one to three expressions, representing the number of "blockIdx.x", "blockIdx.y", and "blockIdx.z" respectively. args : List[Any] Arguments to the kernel function. kwargs : Dict[str, Any] Additional options for the kernel compilation. """ triton_kernel, kernel_args = self._generate_triton_kernel(self.func, *args, **kwargs) kernel_metadata = triton_kernel.metadata ptx = triton_kernel.asm["ptx"] assert kernel_metadata.num_ctas == 1, "Cluster is not supported" num_warps = kernel_metadata.num_warps grid = launch_args launch_param_tags = ["threadIdx.x"] + ["blockIdx.x", "blockIdx.y", "blockIdx.z"][ : len(grid) ] launch_args = [num_warps * 32] + list(grid) kernel_arg_types = [] for arg in kernel_args: if isinstance(arg, int): kernel_arg_types.append("int64") elif isinstance(arg.ty, PointerType): kernel_arg_types.append("handle") else: assert is_prim_expr(arg) kernel_arg_types.append(str(arg.ty.dtype)) if triton_kernel.metadata.shared > 0: # Add shared memory size to the launch arguments launch_param_tags.append("tirx.use_dyn_shared_memory") launch_args.append(triton_kernel.metadata.shared) kernel_module = self._create_cuda_module( ptx, kernel_arg_types, launch_param_tags, triton_kernel.name ) return triton_kernel.name, kernel_module, kernel_args + launch_args def _generate_triton_kernel( self, func, *args, **kwargs ) -> tuple["triton.compiler.CompiledKernel", list[tirx.Expr]]: """Deduce the kernel signature and generate the Triton kernel""" kernel_params = func.params assert len(kernel_params) == len(args), ( f"Number of arguments does not match, expected {len(kernel_params)}, got {len(args)}" ) signature = {} constants = {} kernel_args = [] # Arguments to invoke the kernel for i, arg in enumerate(args): if kernel_params[i].is_constexpr: constants[kernel_params[i].name] = get_const_int(arg) signature[kernel_params[i].name] = "constexpr" kernel_args.append(arg) continue if isinstance(arg.ty, PointerType): assert isinstance(arg, tirx.Var) assert isinstance(arg.ty.element_type, PrimType) elem_type = arg.ty.element_type.dtype pointer_type = "*" + type_canonicalisation_dict[elem_type] signature[kernel_params[i].name] = pointer_type else: assert is_prim_expr(arg) signature[kernel_params[i].name] = type_canonicalisation_dict[arg.ty.dtype] kernel_args.append(arg) # TODO: Support default argument in the kernel # TODO: Add specialization for aligned buffer pointers source = triton.compiler.ASTSource(fn=func, signature=signature, constexprs=constants) compiled = triton.compiler.compile(source, options=kwargs) return compiled, kernel_args