Files
apache--tvm/python/tvm/ir/utils.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

162 lines
5.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.
"""Utilities shared across TVM IR packages."""
from typing import TypeVar
T = TypeVar("T")
def derived_object(cls: type[T]) -> type[T]:
"""A decorator to register derived subclasses for TVM objects.
Parameters
----------
cls : type
The derived class to be registered.
Returns
-------
cls : type
The decorated TVM object.
Example
-------
.. code-block:: python
@register_object("s_tir.meta_schedule.PyRunner")
class _PyRunner(meta_schedule.Runner):
def __init__(self, f_run: Callable = None):
self.__init_handle_by_constructor__(_ffi_api.RunnerPyRunner, f_run)
class PyRunner:
_tvm_metadata = {
"cls": _PyRunner,
"methods": ["run"]
}
def run(self, runner_inputs):
raise NotImplementedError
@derived_object
class LocalRunner(PyRunner):
def run(self, runner_inputs):
...
"""
import functools # pylint: disable=import-outside-toplevel
import weakref # pylint: disable=import-outside-toplevel
def _extract(inst: type, name: str):
"""Extract function from intrinsic class."""
def method(*args, **kwargs):
return getattr(inst, name)(*args, **kwargs)
for inherit_cls, base_cls in zip(cls.__mro__, cls.__mro__[1:]):
# extract functions that differ from the base class
if not hasattr(base_cls, name):
continue
if getattr(base_cls, name) is getattr(inherit_cls, name):
continue
return method
# for task scheduler return None means calling default function
# otherwise it will trigger a RuntimeError of method not implemented
# on the c++ side when you call the method
return None
assert isinstance(cls.__base__, type)
if hasattr(cls, "_type") and cls._type == "TVMDerivedObject": # type: ignore
raise TypeError(
f"Inheritance from a decorated object `{cls.__name__}` is not allowed. "
f"Please inherit from `{cls.__name__}._cls`."
)
assert hasattr(cls, "_tvm_metadata"), (
"Please use the user-facing method overriding class, i.e., PyRunner."
)
base = cls.__base__
metadata = getattr(base, "_tvm_metadata")
fields = metadata.get("fields", [])
methods = metadata.get("methods", [])
base_cls = metadata["cls"]
slots = []
if getattr(base_cls, "__dictoffset__", 0) == 0:
slots.append("__dict__")
if getattr(base_cls, "__weakrefoffset__", 0) == 0:
slots.append("__weakref__")
class TVMDerivedObject(base_cls): # type: ignore
"""The derived object to avoid cyclic dependency."""
__slots__ = tuple(slots)
_cls = cls
_type = "TVMDerivedObject"
def __init__(self, *args, **kwargs):
"""Constructor."""
self._inst = cls(*args, **kwargs)
super().__init__(
# the constructor's parameters, builder, runner, etc.
*[getattr(self._inst, name) for name in fields],
# the function methods, init_with_tune_context, build, run, etc.
*[_extract(self._inst, name) for name in methods],
)
# for task scheduler hybrid funcs in c++ & python side
# using weakref to avoid cyclic dependency
self._inst._outer = weakref.ref(self)
def __getattr__(self, name):
import inspect # pylint: disable=import-outside-toplevel
try:
# fall back to instance attribute if there is not any
# return self._inst.__getattribute__(name)
result = self._inst.__getattribute__(name)
except AttributeError:
result = super().__getattr__(name)
if inspect.ismethod(result):
def method(*args, **kwargs):
return result(*args, **kwargs)
# set __own__ to aviod implicit deconstruction
setattr(method, "__own__", self)
return method
return result
def __setattr__(self, name, value):
if name not in ["_inst", "key", "handle"]:
self._inst.__setattr__(name, value)
else:
super().__setattr__(name, value)
functools.update_wrapper(TVMDerivedObject.__init__, cls.__init__) # type: ignore
TVMDerivedObject.__name__ = cls.__name__
TVMDerivedObject.__doc__ = cls.__doc__
TVMDerivedObject.__module__ = cls.__module__
for key, value in cls.__dict__.items():
if isinstance(value, classmethod | staticmethod):
setattr(TVMDerivedObject, key, value)
return TVMDerivedObject