Files
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

376 lines
10 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.
"""Iterator (quasi)affine mapping patterns."""
from enum import IntEnum
import tvm_ffi
from tvm.ir import Expr
from tvm.runtime import Object
from . import _ffi_api
@tvm_ffi.register_object("arith.IterMapExpr")
class IterMapExpr(Expr):
"""Base class of all IterMap expressions."""
@tvm_ffi.register_object("arith.IterMark")
class IterMark(Object):
"""Mark the source as an iterator in [0, extent).
Parameters
----------
source : Expr.
The source expression.
extent : Expr
The extent of the iterator.
"""
def __init__(self, source, extent):
self.__init_handle_by_constructor__(_ffi_api.IterMark, source, extent)
@tvm_ffi.register_object("arith.IterSplitExpr")
class IterSplitExpr(IterMapExpr):
"""Split of an iterator.
result = floormod(floordiv(source, lower_factor), extent) * scale
Parameters
----------
source : IterMark
The source marked iterator.
lower_factor : Expr
The lower factor to split the domain.
extent : Expr
The extent of the split.
scale : Expr
Additional scale to the split.
"""
def __init__(self, source, lower_factor, extent, scale):
self.__init_handle_by_constructor__(
_ffi_api.IterSplitExpr, source, lower_factor, extent, scale
)
@tvm_ffi.register_object("arith.IterSumExpr")
class IterSumExpr(IterMapExpr):
"""Fuse multiple iterators by summing them with scaling.
result = sum(args) + base
Parameters
----------
args : List[IterSplitExpr]
The input to the sum expression.
base : Expr
The base offset.
"""
def __init__(self, args, base):
self.__init_handle_by_constructor__(_ffi_api.IterSumExpr, args, base)
@tvm_ffi.register_object("arith.IterMapResult")
class IterMapResult(Object):
"""Result of iter map detection."""
class IterMapLevel(IntEnum):
"""Possible kinds of iter mapping check level."""
Bijective = 0
Surjective = 1
NoCheck = 3
@staticmethod
def from_str(name: str):
"""Helper to create level enum from string"""
if name is None:
return IterMapLevel.NoCheck
name = name.lower()
if name == "bijective":
check_level = IterMapLevel.Bijective
elif name == "surjective":
check_level = IterMapLevel.Surjective
elif name == "nocheck":
check_level = IterMapLevel.NoCheck
else:
raise ValueError(f"Unknown check level {name}")
return check_level
def detect_iter_map(
indices,
input_iters,
predicate=True,
check_level=IterMapLevel.Surjective,
simplify_trivial_iterators=True,
analyzer=None,
):
"""Detect if indices can be written as mapped iters from input iters
Parameters
----------
indices : List[Expr]
The input indices
input_iters : Map[tvm.tirx.Var, Range]
The domain of each input iterators.
predicate : Expr
The predicate constraints on the input iterators
check_level : Union[str, IterMapLevel]
Checking level of iteration mapping
simplify_trivial_iterators: bool
If true, iterators with extent of 1 will be replaced with a
constant value.
analyzer : Optional[tvm.arith.Analyzer]
The analyzer to use. When provided, its accumulated bindings and
constraints are reused; otherwise a fresh analyzer is created.
Returns
-------
results : IterMapResult
The iter map matching result.
The result's .indices is empty array if no match can be found.
"""
if isinstance(check_level, str):
check_level = IterMapLevel.from_str(check_level)
elif check_level is None:
check_level = IterMapLevel.NoCheck
return _ffi_api.DetectIterMap(
indices, input_iters, predicate, check_level, simplify_trivial_iterators, analyzer
)
def normalize_to_iter_sum(index, input_iters, analyzer=None):
"""Normalize expr to iter sum.
The normalized result ensures that
each scale is in the form of (symbol_prod) * cscale
It will also sort in desc order by cscale then len(symbol_prod).
Parameters
----------
index : Expr
The input index
input_iters : Map[tvm.tirx.Var, Range]
The domain of each input iterators.
analyzer : Optional[tvm.arith.Analyzer]
The analyzer to use. When provided, its accumulated bindings and
constraints are reused; otherwise a fresh analyzer is created.
Returns
-------
iter_sum: IterSumExpr
The result iter sum
Note
----
This function does best effort detection, so some undetected
part can go into iter_sum.base
This function is useful to decide the stride multiplier and
division factor in buffer access patterns.
"""
return _ffi_api.NormalizeToIterSum(index, input_iters, analyzer)
def iter_map_simplify(
indices,
input_iters,
predicate=True,
check_level=IterMapLevel.Surjective,
simplify_trivial_iterators=True,
analyzer=None,
):
"""Simplify the indices using iter map detection.
Parameters
----------
indices : List[Expr]
The input indices
input_iters : Map[tvm.tirx.Var, Range]
The domain of each input iterators.
predicate : Expr
The predicate constraints on the input iterators
check_level : Union[str, IterMapLevel]
Checking level of iteration mapping
simplify_trivial_iterators: bool
If true, iterators with extent of 1 will be replaced with a
constant value.
analyzer : Optional[tvm.arith.Analyzer]
The analyzer to use. When provided, its accumulated bindings and
constraints are reused; otherwise a fresh analyzer is created.
Returns
-------
results : IterMapResult
The iter map matching result.
The result's .indices is empty array if no match can be found.
"""
if isinstance(check_level, str):
check_level = IterMapLevel.from_str(check_level)
elif check_level is None:
check_level = IterMapLevel.NoCheck
return _ffi_api.IterMapSimplify(
indices, input_iters, predicate, check_level, simplify_trivial_iterators, analyzer
)
def normalize_iter_map_to_expr(expr):
"""Given an IterMapExpr, transform it to normal Expr
Parameters
----------
expr : IterMapExpr
the input IterMapExpr
Returns
-------
result : Expr
the corresponding normal Expr
"""
return _ffi_api.NormalizeIterMapToExpr(expr)
def subspace_divide(
bindings,
input_iters,
sub_iters,
predicate=True,
check_level=IterMapLevel.Surjective,
simplify_trivial_iterators=True,
analyzer=None,
):
"""Detect if bindings can be written as
``[a_0*e_0 + b_0 + c_0, a_1*e_1 + b_1, ..., a_n*e_n + b_n]``
where::
a = some-quasi-affine-iter-map(input_iters set_minus sub_iters)
b = some-quasi-affine-iter-map(sub_iters)
c is constant symbols
e is the extent of b
For example::
z*12 + y*3 + x + c = (z*4+y)*3 + x
bindings = [z*12 + y*3 + x + c]
input_iters = [z, y, x]
sub_iter = [x]
Then the result will be [a, b] where
a = [z*4 + y]
b = [x]
Parameters
----------
bindings : List[Expr]
The input bindings
input_iters : Map[tvm.tirx.Var, Range]
The domain of input iterator, which is the basis of the whole space
sub_iters : Array[tvm.tirx.Var]
The subset of input_iters, which is the basis of the subspace
predicate : Expr
The predicate constraints on the input iterators
check_level : Union[str, IterMapLevel]
Checking level of iteration mapping
simplify_trivial_iterators: bool
If true, iterators with extent of 1 will be replaced with a
constant value.
analyzer : Optional[tvm.arith.Analyzer]
The analyzer to use. When provided, its accumulated bindings and
constraints are reused; otherwise a fresh analyzer is created.
Returns
-------
results : List[List[Expr]]
The result list has length ``len(bindings) + 1``.
- ``[0, len(bindings))``: The iter map matching result.
The inner list is of length 2. The first expr is the basis
of the quotient space. The second expr is the basis of the subspace.
- ``len(bindings)``: the predicate of outer space and inner space.
- Empty array if no match can be found.
"""
if isinstance(check_level, str):
check_level = IterMapLevel.from_str(check_level)
return _ffi_api.SubspaceDivide(
bindings,
input_iters,
sub_iters,
predicate,
check_level,
simplify_trivial_iterators,
analyzer,
)
def inverse_affine_iter_map(iter_map, outputs):
"""Apply the inverse of the affine transformation to the outputs.
Similar to the back-propagation, starting from the outputs, it visits the DAG of the expressions
in reverse topology order and applies the inverse of the affine transformation until it reaches
the input. The affine iter map is required to be bijective.
For example, iter_map = [l0 // 16, l0 % 16], outputs = [output_0, output_1],
the affine transformation specified by `iter_map` will be applied to `outputs` and the result
will be {l0: ((output_0*16) + output_1)}.
See also :any:`detect_iter_map`.
Parameters
----------
iter_map : List[IterSumExpr]
The bijective affine iter map.
outputs : List[Expr]
The outputs of the affine transformation.
Returns
-------
results : Map[tvm.tirx.Var, Expr]
The map from the input to the transformed result.
"""
return _ffi_api.InverseAffineIterMap(iter_map, outputs)