Files
apache--tvm/python/tvm/s_tir/analysis/__init__.py
T
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

217 lines
7.2 KiB
Python

# isort: skip_file
# 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.
"""Analysis utilities for Schedulable TensorIR (S-TIR)."""
# pylint: disable=invalid-name
from typing import Optional, Union
import tvm
from tvm.ir import IRModule
from tvm.tirx.expr import Var
from tvm.tirx.stmt import SBlock, BufferRegion
from tvm.tirx import Buffer, Stmt
from tvm.tirx.function import PrimFunc
from . import _ffi_api
def get_sblock_access_region(
block: SBlock, buffer_var_map: dict[Var, Buffer]
) -> list[list[BufferRegion]]:
"""Detect which regions of tensors in this block are read or written to.
Regions are sorted by order of appearance in the AST.
Parameters
----------
block: tvm.tirx.SBlock
The block in which we are detecting read/write regions.
buffer_var_map : Dict[Var, Buffer]
The outside buffers which may access the block. Mapping from buffer var to the buffer
Returns
-------
result : List[List[BufferRegion]]
Array of access regions. There are three arrays of BufferRegion:
- first: read regions
- second: write regions
- third: opaque regions
"""
return _ffi_api.GetSBlockAccessRegion(block, buffer_var_map) # type: ignore
def get_sblock_read_write_region(
block: SBlock, buffer_var_map: dict[Var, Buffer]
) -> list[list[BufferRegion]]:
"""Auto detect the block read/write region according to its body stmt.
An opaque access will be counted as both a read and a write access
Parameters
----------
block: tvm.tirx.SBlock
The block in which we are detecting read/write regions.
buffer_var_map : Dict[Var, Buffer]
The outside buffers which may access the block. Mapping from buffer var to the buffer
Returns
-------
result : List[List[BufferRegion]]
An array only consisting of the read regions and write regions of the input block
"""
return _ffi_api.GetSBlockReadWriteRegion(block, buffer_var_map) # type: ignore
def detect_buffer_access_lca(func: PrimFunc) -> dict[Buffer, Stmt]:
"""Detect the lowest common ancestor(LCA) of buffer access, including both high-level
access (BufferLoad, BufferStore) and low-level access (BufferLoad, BufferStore and opaque
access).
The LCA may be a For loop or a Block.
Parameters
----------
func: tvm.tirx.PrimFunc
The function to be detected.
Returns
-------
result : Dict[Buffer, Stmt]
Map from buffer to the LCA of all access to it.
"""
return _ffi_api.detect_buffer_access_lca(func) # type: ignore # pylint: disable=no-member
def find_anchor_sblock(mod: IRModule) -> SBlock | None:
"""Find the "anchor block" of the given module.
We define the anchor block to be the block with (1) an init statement and (2) having
the biggest flops count. The latter condition is only used when there are multiple blocks
with an init statement.
For example, if the input module is conv2d + fused spatial blocks, conv2d is the anchor block.
The input module may not contain more than one such block. For example, a module having
two conv2d is not allowed as an input.
However, a module created from winograd convolution has multiple blocks with an init statement
(input transform, batched GEMM, and output transform). We use the second condition, the flops
count, to determine that the batched GEMM block is the anchor block.
Parameters
----------
mod: tvm.ir.IRModule
The input TIR module.
Returns
-------
anchor_block: Optional[SBlock]
The anchor block if found, None otherwise.
"""
return _ffi_api.find_anchor_sblock(mod) # type: ignore # pylint: disable=no-member
def verify_gpu_code(func: PrimFunc, constraints: dict[str, int]) -> bool:
"""Verify if module contains illegal host side direct memory access.
Parameters
----------
func: tvm.tirx.PrimFunc
The module to be verified.
constraints : Dict[str, int]
The attribute constraints.
Returns
-------
result : bool
The result of verification.
"""
return _ffi_api.verify_gpu_code(func, constraints) # type: ignore
def calculate_allocated_bytes(
func_or_mod: PrimFunc | IRModule,
) -> dict[str, dict[str, int]]:
"""Calculate allocated memory per memory scope required by TIR PrimFuncs.
Parameters
----------
func_or_mod: Union[PrimFunc, IRModule]
The function or module to be detected. If a module is passed, allocated
memory is calculated for all PrimFuncs inside the module
Returns
-------
result : Dict[str, Dict[str, int]]
Allocated memory size per scope in bytes for each function in the IRModule returned as a
dict with function names as keys and a dict of allocated sizes as values. If a single
PrimFunc is passed, the function name is returned as "main"
"""
if not isinstance(func_or_mod, PrimFunc | IRModule):
raise TypeError(
f"Expected argument to be PrimFunc or IRModule, but received {type(func_or_mod)}"
)
return _ffi_api.calculate_allocated_bytes(func_or_mod) # type: ignore
def estimate_tir_flops(stmt_or_mod: Stmt | IRModule) -> float:
"""Estimate the FLOPs of a TIR fragment.
Parameters
----------
stmt_or_mod: Union[Stmt, IRModule]
The TIR fragment or IRModule to be estimated.
Returns
-------
flops: float
The estimated FLOPs.
"""
return _ffi_api.EstimateTIRFlops(stmt_or_mod) # type: ignore # pylint: disable=no-member
def OOBChecker():
"""Detect out of bounds memory access in arrays.
Returns
-------
fpass : tvm.transform.Pass
The result pass
"""
return _ffi_api.OOBChecker() # type: ignore
def get_vtcm_compaction_passes() -> list[tvm.transform.Pass]:
"""Utility function to get the list of lowering passes to be applied to calculate the compacted
VTCM allocation size
Returns
-------
result : List[tvm.transform.Pass]
returns list of passes
"""
return _ffi_api.get_vtcm_compaction_passes() # type: ignore # pylint: disable=no-member
def is_pure_function(func: PrimFunc) -> bool:
"""Checks if the function is a pure function"""
return _ffi_api.is_pure_function(func, False) # type: ignore # pylint: disable=no-member
def assert_pure_function(func: PrimFunc) -> bool:
"""Asserts that the function is a pure function"""
return _ffi_api.is_pure_function(func, True) # type: ignore # pylint: disable=no-member