529 lines
13 KiB
Python
529 lines
13 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.
|
|
"""Wrapping existing transformations."""
|
|
# pylint: disable=invalid-name, unsupported-binary-operation
|
|
|
|
import enum
|
|
from collections.abc import Callable
|
|
|
|
import tvm_ffi as _ffi
|
|
|
|
from . import _ffi_api
|
|
from . import function_pass as _fpass
|
|
|
|
|
|
def Apply(ftransform):
|
|
"""Apply ftransform to each function in the Module.
|
|
|
|
This function is a thin wrapper around tvm.tirx.transform.prim_func_pass
|
|
|
|
Parameters
|
|
----------
|
|
ftransform: tvm.tirx.PrimFunc -> tvm.tirx.PrimFunc
|
|
The transformation pass.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
|
|
# pylint: disable=unused-argument
|
|
def _transform(func, mod, ctx):
|
|
return ftransform(func)
|
|
|
|
return _fpass.prim_func_pass(_transform, opt_level=0, name="Apply") # type: ignore
|
|
|
|
|
|
def VectorizeLoop(enable_vectorize: bool = True):
|
|
"""Lower vectorization loops.
|
|
|
|
Parameters
|
|
----------
|
|
enable_vectorize : bool
|
|
Whether vectorization is enabled.
|
|
Will lower to scalar loop when it is turned off.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.VectorizeLoop(enable_vectorize) # type: ignore
|
|
|
|
|
|
def StorageRewrite():
|
|
"""Rewrite storage allocation pattern.
|
|
|
|
Moves the allocation to outer most possible scope.
|
|
Trying to share space between allocations to make
|
|
a static allocation plan when possible.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.StorageRewrite() # type: ignore
|
|
|
|
|
|
def InlinePrivateFunctions():
|
|
"""Inline calls to private functions
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.InlinePrivateFunctions() # type: ignore
|
|
|
|
|
|
def PointerValueTypeRewrite():
|
|
"""
|
|
Rewrite the pointer content type of arguments, as well as Alloc internal to the function to use
|
|
the most frequently accessed type for load/store to avoid pointer casting in backend when
|
|
possible.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.PointerValueTypeRewrite() # type: ignore
|
|
|
|
|
|
@_ffi.register_object("tirx.transform.UnrollLoopConfig")
|
|
class UnrollLoopConfig(_ffi.Object):
|
|
"""Config for unroll loop pass"""
|
|
|
|
|
|
def UnrollLoop():
|
|
"""Unroll the constant loop marked by unroll.
|
|
|
|
This pass also automatically attach pragma unroll tag to loops which meets the standard.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.UnrollLoop() # type: ignore
|
|
|
|
|
|
@_ffi.register_object("tirx.transform.RemoveNoOpConfig")
|
|
class RemoveNoOpConfig(_ffi.Object):
|
|
"""Config for remove no op pass"""
|
|
|
|
|
|
def RemoveNoOp():
|
|
"""Remove No Op from the Stmt.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.RemoveNoOp() # type: ignore
|
|
|
|
|
|
def RemoveAssume():
|
|
"""Remove all instances of builtin::assume
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.RemoveAssume() # type: ignore
|
|
|
|
|
|
def BF16ComputeLegalize():
|
|
"""Legalize bf16 compute Ops.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.BF16ComputeLegalize() # type: ignore
|
|
|
|
|
|
def FP8ComputeLegalize(promote_dtype: str = "float32"):
|
|
"""Legalize fp8 compute Ops.
|
|
|
|
Parameters
|
|
----------
|
|
promote_dtype : str
|
|
The data type we promote fp8 to, options: float16/float32.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.FP8ComputeLegalize(promote_dtype) # type: ignore
|
|
|
|
|
|
def BF16StorageLegalize():
|
|
"""Legalize bf16 storage types to u16.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.BF16StorageLegalize() # type: ignore
|
|
|
|
|
|
def FP8StorageLegalize():
|
|
"""Legalize fp8 storage types to u8.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.FP8StorageLegalize() # type: ignore
|
|
|
|
|
|
def CommonSubexprElim():
|
|
"""Replace redundant computations by new variables.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.CommonSubexprElim() # type: ignore
|
|
|
|
|
|
@_ffi.register_object("tirx.transform.StmtSimplifyConfig")
|
|
class StmtSimplifyConfig(_ffi.Object):
|
|
"""Config for stmt simplify pass"""
|
|
|
|
|
|
def StmtSimplify():
|
|
"""Run statement-level arithmetic simplifications on the TIR PrimFunc.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.StmtSimplify() # type: ignore
|
|
|
|
|
|
def ConvertSSA():
|
|
"""Convert an IRModule to be SSA form.
|
|
|
|
This pass handles cases where the same `tirx.Var` appears in
|
|
multiple functions within the same module. For example, after
|
|
extracting a fragment from one function into another, where the
|
|
same `tirx.Var` may be defined both as within the body of the
|
|
original function, and as a parameter within the hoisted function.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
|
|
"""
|
|
return _ffi_api.ConvertSSA() # type: ignore
|
|
|
|
|
|
def MakePackedAPI():
|
|
"""Transform the PrimFuncs in the module to a packed func API.
|
|
|
|
Prior to this pass, the PrimFunc may have Buffer arguments defined
|
|
in the `PrimFuncNode::buffer_map`. This pass consumes the
|
|
`buffer_map`, using it to generate arguments that implement
|
|
the packed based TVM FFI API.
|
|
|
|
For static shapes, the `BufferNode::shape`, `BufferNode::strides`,
|
|
and `BufferNode::elem_offset` member variables are used to
|
|
generate runtime checks on the corresponding member variables in
|
|
the user-provided `DLTensor*` or `tvm.runtime.tensor` argument. (e.g. A
|
|
PrimFunc that accepts a buffer of shape `[16,32]` validates that
|
|
the `DLTensor::shape` array is `[16,32]`.)
|
|
|
|
For dynamic Buffers, in which one or more of these `BufferNode` member
|
|
variables use `tirx.Var` that are not defined by other PrimFunc
|
|
parameters, these are instead used to define the variables based on
|
|
the corresponding `DLTensor` members. (e.g. A PrimFunc that accepts a
|
|
buffer of shape `[tirx.Var("n"), tirx.Var("m")]`, when passed a
|
|
`DLTensor` of shape `[16,32]`, will define `n = 16` and `n=32`, based
|
|
on the argument's shape.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.MakePackedAPI() # type: ignore
|
|
|
|
|
|
def SplitHostDevice():
|
|
"""Annotate, split, and lower host/device functions.
|
|
|
|
This pass first annotates device regions within host functions,
|
|
then splits them into host and device-side PrimFuncs, and finally
|
|
lowers host-to-device calls into the device kernel launch ABI.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.SplitHostDevice() # type: ignore
|
|
|
|
|
|
def SkipAssert():
|
|
"""Skip assert stmt.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.SkipAssert() # type: ignore
|
|
|
|
|
|
def LowerWarpMemory():
|
|
"""Lower warp memory access to low-level device related function calls.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.LowerWarpMemory() # type: ignore
|
|
|
|
|
|
def LowerTVMBuiltin():
|
|
"""Lower tvm builtin intrinsics.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.LowerTVMBuiltin() # type: ignore
|
|
|
|
|
|
def LowerIntrin():
|
|
"""Lower target specific intrinsic calls.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.LowerIntrin() # type: ignore
|
|
|
|
|
|
def NarrowDataType(target_bits: int):
|
|
"""Narrow down Expr datatype in stmt to target_bits.
|
|
|
|
Parameters
|
|
----------
|
|
target_bits : int
|
|
The target bit configuration.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
|
|
Note
|
|
----
|
|
Run this pass after FlattenBuffer.
|
|
"""
|
|
return _ffi_api.NarrowDataType(target_bits) # type: ignore
|
|
|
|
|
|
def ForceNarrowIndexToInt32():
|
|
"""Force narrow down indexing expressions and integer buffers to int32 dtype.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
|
|
Note
|
|
----
|
|
This pass should not be used in default cases.
|
|
"""
|
|
return _ffi_api.ForceNarrowIndexToInt32() # type: ignore
|
|
|
|
|
|
def VerifyMemory():
|
|
"""Verify if func contains illegal host side direct memory access.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.VerifyMemory() # type: ignore
|
|
|
|
|
|
@_ffi.register_object("s_tir.transform.HoistIfThenElseConfig")
|
|
class HoistIfThenElseConfig(_ffi.Object):
|
|
"""Config for hoist if then else pass"""
|
|
|
|
|
|
class HoistedConditionals(enum.Flag):
|
|
"""Flags for use in HoistExpressionConfig.conditional_types
|
|
|
|
Each bitflag represents a type of expression that should be
|
|
hoisted to the outermost loop possible.
|
|
"""
|
|
|
|
Never = 0
|
|
""" No hoisting of conditionals """
|
|
|
|
IfElseStmt = 1
|
|
""" If set, look for hoist candidates in IfElseStmt """
|
|
|
|
IfElseExpr = 2
|
|
""" If set, look for hoist candidates in tirx.if_then_else """
|
|
|
|
BooleanExpression = 4
|
|
""" If set, look for hoist candidates in all boolean expressions """
|
|
|
|
UsingBlockVar = 8
|
|
""" If set, allow hoisting of conditionals that use a block variable (e.g. threadIdx.x) """
|
|
|
|
All = IfElseStmt | IfElseExpr | BooleanExpression | UsingBlockVar
|
|
""" Enable all hoisting of conditionals"""
|
|
|
|
|
|
class HoistedLetBindings(enum.Flag):
|
|
"""Flags for use in HoistExpressionConfig.let_binding_types
|
|
|
|
Each bitflag represents a type of let binding expression that should be
|
|
hoisted to the outermost loop possible.
|
|
"""
|
|
|
|
Never = 0
|
|
""" No hoisting of let bindings """
|
|
|
|
RequiredByConditional = 1
|
|
""" Bindings that are used by a hoisted conditional """
|
|
|
|
Bind = 2
|
|
""" Bindings occurring in Bind nodes """
|
|
|
|
LetExpr = 4
|
|
""" Bindings occurring in Let expressions """
|
|
|
|
All = RequiredByConditional | Bind | LetExpr
|
|
""" Enable all hoisting of let bindings """
|
|
|
|
|
|
@_ffi.register_object("s_tir.transform.HoistExpressionConfig")
|
|
class HoistExpressionConfig(_ffi.Object):
|
|
"""Config for hoist expression pass"""
|
|
|
|
|
|
def FlattenBuffer():
|
|
"""Flatten the multi-dimensional BufferLoad and BufferStore to single dimensional
|
|
BufferLoad/BufferStore for the TIR not contains opaque block.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.FlattenBuffer() # type: ignore
|
|
|
|
|
|
def BindTarget(target):
|
|
"""Annotate a PrimFunc with a given target.
|
|
Parameters
|
|
-------
|
|
target : tvm.target.Target
|
|
target
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.BindTarget(target) # type: ignore
|
|
|
|
|
|
def AnnotateEntryFunc():
|
|
"""Set a PrimFunc as the entry point if it is only function in IRModule.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.AnnotateEntryFunc() # type: ignore
|
|
|
|
|
|
def Filter(fcond: Callable):
|
|
"""Filter out PrimFuncs that does not satisfy the given condition.
|
|
`fcond` should be a function that takes a primfunc and returns boolean.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.Filter(fcond) # type: ignore
|
|
|
|
|
|
def TilePrimitiveDispatch():
|
|
"""Lower TIRx tile primitive calls through the active backend dispatch table.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.TilePrimitiveDispatch() # type: ignore
|
|
|
|
|
|
def LowerTIRx():
|
|
"""Lower TIR to a lower-level IR.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.LowerTIRx() # type: ignore
|
|
|
|
|
|
def LowerTIRxOpaque():
|
|
"""Lower opaque constructs in TIRX programs.
|
|
|
|
Handles AllocBuffer lowering, For(thread_binding) to AttrStmt(thread_extent)
|
|
conversion, unit loop elimination, and pragma annotation handling.
|
|
This is the tirx-specific counterpart of s_tir.LowerOpaqueBlock,
|
|
without any SBlock/SBlockRealize handling.
|
|
|
|
Returns
|
|
-------
|
|
fpass : tvm.transform.Pass
|
|
The result pass
|
|
"""
|
|
return _ffi_api.LowerTIRxOpaque() # type: ignore
|