Files
apache--tvm/python/tvm/s_tir/schedule/state.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

239 lines
8.5 KiB
Python

# 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=invalid-name
"""This file defines ScheduleState, the core data structure of TensorIR scheduling."""
from collections import namedtuple
from enum import IntEnum
from tvm_ffi import register_object
from tvm.ir import IRModule
from tvm.runtime import Object
from tvm.tirx import For, PrimFunc, SBlock, SBlockRealize
from ..sblock_scope import SBlockScope, StmtSRef
from . import _ffi_api
CachedFlags = namedtuple("CachedFlags", ["affine_binding", "region_cover", "stage_pipeline"])
class ScheduleDebugMask(IntEnum):
"""The bitmask of the `debug_mask` flag in the ScheduleState class.
If the `debug_mask` flag has a certain bit on, then the correpsonding
verification pass will be conducted. For example, if `(debug_mask & VERIFY_SREF_TREE) != 0`,
then the correctness of the sref tree will be verified after each schedule instruction.
Attributes
----------
VERIFY_SREF_TREE : int = 1
Verify the correctness of the sref tree
VERIFY_CACHED_FLAGS : int = 2
Verify the correctness of affine_binding, region_cover and stage_pipeline
"""
VERIFY_SREF_TREE = 1
VERIFY_CACHED_FLAGS = 2
def _parse_mod(mod: PrimFunc | IRModule) -> IRModule:
if isinstance(mod, PrimFunc):
mod = IRModule({"main": mod})
if not isinstance(mod, IRModule):
raise TypeError(f"Expected `mod` to be PrimFunc or IRModule, but gets: {mod}")
return mod
def _parse_debug_mask(debug_mask: str | int) -> int:
if isinstance(debug_mask, str):
if debug_mask == "all":
debug_mask = ScheduleDebugMask.VERIFY_SREF_TREE | ScheduleDebugMask.VERIFY_CACHED_FLAGS
elif debug_mask == "none":
debug_mask = 0
else:
raise ValueError(f"Unrecognizable `debug_mask`: {debug_mask}")
if not isinstance(debug_mask, bool) and not isinstance(debug_mask, int):
raise TypeError(f"`debug_mask` should be integer or boolean, but gets: {debug_mask}")
return debug_mask
def _parse_enable_checks(enable_checks: bool) -> bool:
if not isinstance(enable_checks, bool):
raise TypeError(f"enable_checks only accepts bool value, got {type(enable_checks)} instead")
return enable_checks
@register_object("s_tir.ScheduleState")
class ScheduleState(Object):
"""The state of scheduling, which exposes a `Replace` method as
the primary resort for all the scheduling primitives to manipulate the TensorIR.
The data structure contains the following information
1) The AST being scheduled (mod)
2) The sref tree of schedulable statements (indicated by the srefs)
3) The dependency information of each block scope (block_info)
4) A reverse mapping from the AST nodes to that in the sref tree (get_sref)
5) A debug flag, if set, extra checking is enabled (debug_mask)
6) A enable check flag, if False, some prerequisite checks are disabled.
Parameters
----------
mod : IRModule
The AST of the module being scheduled
debug_mask : int
Do extra correctness checking after the object construction
and each time after calling the Replace method.
enable_check : bool
Indicates whether we enable prerequisite checks for some schedule primitives or not,
defaults to `True`.
"""
mod: IRModule
debug_mask: int
def __init__(
self,
mod: PrimFunc | IRModule,
*,
debug_mask: str | int = "none",
enable_check: bool = True,
) -> None:
"""Construct a schedule state from an IRModule or a PrimFunc
Parameters
----------
mod : Union[PrimFunc, IRModule]
The IRModule or PrimFunc to be scheduled
debug_mask : Union[str, int]
Do extra correctness checking after the class creation and each time
after calling the Replace method.
Possible choices of `debug_mask`:
1) "all" - Turn on all the checks
2) "none" - Turn off all the checks
3) An integer - Turn on checks according to the bitmasks provided in ScheduleDebugMask
"""
self.__init_handle_by_constructor__(
_ffi_api.ScheduleState, # type: ignore # pylint: disable=no-member
_parse_mod(mod),
_parse_debug_mask(debug_mask),
_parse_enable_checks(enable_check),
)
def get_sref(self, stmt: SBlock | For) -> StmtSRef | None:
"""Return the corresponding sref that points to the stmt
Parameters
----------
stmt : Union[Block, For]
The schedulable statement in the TensorIR to be retrieved for its sref
Returns
-------
sref : StmtSRef
The corresponding sref
"""
return _ffi_api.ScheduleStateGetSRef(self, stmt) # type: ignore # pylint: disable=no-member
def get_sblock_scope(self, block_sref: StmtSRef) -> SBlockScope:
"""Get the SBlockScope correpsonding to the block sref
Parameters
----------
block_sref : StmtSRef
The block sref to be retrieved
Returns
-------
sref : StmtSRef
The corresponding sref
"""
return _ffi_api.ScheduleStateGetSBlockScope( # type: ignore # pylint: disable=no-member
self, block_sref
)
def _get_cached_flags(self, block_sref: StmtSRef) -> CachedFlags:
"""Get the cached flags of the corresponding block
Parameters
----------
block_sref : StmtSRef
The block sref to be retrieved
Returns
-------
flags : CachedFlags
Three flags: affine_binding, region_cover, stage_pipeline
Note
----
It is an API intended for internal testing use.
"""
(
affine_binding,
region_cover,
stage_pipeline,
) = _ffi_api.ScheduleStateGetCachedFlags( # type: ignore # pylint: disable=no-member
self, block_sref
)
return CachedFlags(
affine_binding=bool(affine_binding.value),
region_cover=bool(region_cover.value),
stage_pipeline=bool(stage_pipeline.value),
)
def replace(
self,
src_sref: StmtSRef,
tgt_stmt: SBlock | For | SBlockRealize,
block_sref_reuse: dict[SBlock, SBlock] | None = None,
) -> None:
"""
Replace the part of the AST, as being pointed to by `src_sref`,
with a specific statement `tgt_stmt`, and maintain the sref tree accordingly.
Replace will try to perform copy on write as much as possible when the ScheduleState holds
the only copy to the IRModule and IR nodes.
Only 3 types of replacements are allowed: from `src_sref->stmt` to `tgt_stmt`.
1) SBlock -> SBlock
2) Loop -> Loop
3) Loop -> BlockRealize
Parameters
----------
src_sref : StmtSRef
The sref to the statement to be replaced in the TensorIR AST
tgt_stmt : Union[Block, For, BlockRealize]
The statement to be replaced to
block_sref_reuse : Optional[Dict[Block, Block]] = None
Maps an old block (to be replaced in the subtree under `src_sref->stmt`)
to a new block (replaced to, in the subtree under `tgt_stmt`), and enforces
reuse of srefs between them (rather than create new srefs) i.e. after being replaced,
the sref that points to the old block will point to the new one
Note
----
The reuse of loop srefs are detected automatically according to the reuse of loop vars.
"""
if block_sref_reuse is None:
block_sref_reuse = {}
_ffi_api.ScheduleStateReplace( # type: ignore # pylint: disable=no-member
self, src_sref, tgt_stmt, block_sref_reuse
)