164 lines
5.6 KiB
Python
164 lines
5.6 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.
|
|
"""TIR specific function pass support."""
|
|
|
|
import functools
|
|
import inspect
|
|
from collections.abc import Callable
|
|
|
|
import tvm_ffi
|
|
|
|
from tvm.ir.transform import Pass, PassInfo
|
|
|
|
from . import _ffi_api
|
|
|
|
|
|
@tvm_ffi.register_object("tirx.PrimFuncPass")
|
|
class PrimFuncPass(Pass):
|
|
"""A pass that works on each :py:func:`tvm.tirx.PrimFunc` in a module. A function
|
|
pass class should be created through py:func:`tvm.tirx.transform.function_pass`.
|
|
"""
|
|
|
|
|
|
def _wrap_class_function_pass(pass_cls, pass_info):
|
|
"""Wrap a python class as function pass"""
|
|
|
|
class PyFunctionPass(PrimFuncPass):
|
|
"""Internal wrapper class to create a class instance."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
inst = pass_cls(*args, **kwargs)
|
|
|
|
# it is important not to capture self to
|
|
# avoid a cyclic dependency
|
|
def _pass_func(func, mod, ctx):
|
|
return inst.transform_function(func, mod, ctx)
|
|
|
|
self.__init_handle_by_constructor__(
|
|
_ffi_api.CreatePrimFuncPass,
|
|
_pass_func,
|
|
pass_info, # type: ignore
|
|
)
|
|
|
|
self._inst = inst
|
|
|
|
def __getattr__(self, name):
|
|
# fall back to instance attribute if there is not any
|
|
return self._inst.__getattribute__(name)
|
|
|
|
functools.update_wrapper(PyFunctionPass.__init__, pass_cls.__init__)
|
|
PyFunctionPass.__name__ = pass_cls.__name__
|
|
PyFunctionPass.__doc__ = pass_cls.__doc__
|
|
PyFunctionPass.__module__ = pass_cls.__module__
|
|
return PyFunctionPass
|
|
|
|
|
|
def prim_func_pass(
|
|
pass_func=None,
|
|
opt_level: int | None = None,
|
|
name: str | None = None,
|
|
required: list[str] | None = None,
|
|
traceable=False,
|
|
) -> Callable | PrimFuncPass:
|
|
"""Decorate a function pass.
|
|
|
|
This function returns a callback when pass_func
|
|
is provided. Otherwise, it returns the created function pass using the
|
|
given optimization function.
|
|
|
|
Parameters
|
|
----------
|
|
pass_func : Optional[Callable[(tvm.tirx.PrimFunc, IRModule, PassContext) -> tvm.tirx.PrimFunc]]
|
|
The transformation function or class.
|
|
|
|
opt_level : int
|
|
The optimization level of this module pass.
|
|
|
|
name : Optional[str]
|
|
The name of the function pass. The name could be empty. In this case, the
|
|
name of the optimization function will be used as the pass name.
|
|
|
|
required : Optional[List[str]]
|
|
The list of passes that the function pass is dependent on.
|
|
|
|
Returns
|
|
-------
|
|
create_function_pass : Union[Callable, FunctionPass]
|
|
|
|
A decorator will be returned if pass_func is not provided,
|
|
otherwise return the decorated result.
|
|
The returned decorator has two behaviors depending on the input:
|
|
A new FunctionPass will be returned when we decorate a pass function.
|
|
A new FunctionPass class will be returned when we decorate a class type.
|
|
|
|
Examples
|
|
--------
|
|
The following code block decorates a function pass class.
|
|
|
|
.. code-block:: python
|
|
|
|
@tvm.tirx.transform.prim_func_pass(opt_level=1)
|
|
class TestReplaceFunc:
|
|
def __init__(self, new_func):
|
|
self.new_func = new_func
|
|
|
|
def transform_function(self, func, mod, ctx):
|
|
# just for demo purposes
|
|
# transform func to new_func
|
|
return self.new_func
|
|
|
|
The following code creates a function pass by decorating
|
|
a user defined transform function.
|
|
|
|
.. code-block:: python
|
|
|
|
@tvm.tirx.transform.prim_func_pass(opt_level=2)
|
|
def transform(func, mod, ctx):
|
|
# my transformations here.
|
|
return func
|
|
|
|
function_pass = transform
|
|
assert isinstance(function_pass, transform.FunctionPass)
|
|
assert function_pass.info.opt_level == 2
|
|
|
|
# Given a module m, the optimization could be invoked as the following:
|
|
updated_mod = function_pass(m)
|
|
# Now constant folding should have been applied to every function in
|
|
# the provided module m. And the updated module will be returned.
|
|
"""
|
|
|
|
if opt_level is None:
|
|
raise ValueError("Please provide opt_level for the function pass.")
|
|
|
|
required = required if required else []
|
|
if not isinstance(required, list | tuple):
|
|
raise TypeError("Required is expected to be the type of " + "list/tuple.")
|
|
|
|
def create_function_pass(pass_arg):
|
|
"""Internal function that creates a function pass"""
|
|
fname = name if name else pass_arg.__name__
|
|
info = PassInfo(opt_level, fname, required, traceable)
|
|
if inspect.isclass(pass_arg):
|
|
return _wrap_class_function_pass(pass_arg, info)
|
|
if not callable(pass_arg):
|
|
raise TypeError("pass_func must be a callable for Module pass")
|
|
return _ffi_api.CreatePrimFuncPass(pass_arg, info) # type: ignore
|
|
|
|
if pass_func:
|
|
return create_function_pass(pass_func)
|
|
return create_function_pass
|