# 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. """Pattern registry for BYOC backends""" import atexit from collections.abc import Callable, Mapping from tvm.relax.dpl import DFPattern from tvm.relax.transform import FusionPattern from ..expr import Expr from . import _ffi_api _REGISTERED_PATTERN_NAMES: set[str] = set() def _cleanup_registered_patterns(): _ffi_api.RemovePatterns(list(_REGISTERED_PATTERN_NAMES)) # type: ignore # pylint: disable=no-member _CLEANUP_REGISTERED = False def _ensure_cleanup_function_registered(): """ Add a cleanup function to be called on interpreter termination, to remove all patterns registered on the Python side. Without cleaning up those patterns, program will segfault on termination. It's because the check functions of pattern entries are referenced from the static memory of libtvm, thus they will be cleaned up at the very end, making calls to Py_DecRef after Python interpreter terminates. """ global _CLEANUP_REGISTERED # pylint: disable=global-statement if not _CLEANUP_REGISTERED: atexit.register(_cleanup_registered_patterns) _CLEANUP_REGISTERED = True CheckFunc = Callable[[Mapping[DFPattern, Expr], Expr], bool] Pattern = ( FusionPattern | tuple[str, DFPattern] | tuple[str, DFPattern, Mapping[str, DFPattern]] | tuple[str, DFPattern, Mapping[str, DFPattern], CheckFunc] ) def register_patterns(patterns: list[Pattern]): """ Register patterns which will be used to partition the DataflowBlock into subgraphs that are supported by external backends. Parameters ---------- patterns: List[Pattern] Patterns to be registered. Patterns that appear later in the list have higher priority when partitioning DataflowBlock. """ _ensure_cleanup_function_registered() entries = [] for item in patterns: if isinstance(item, FusionPattern): entries.append(item) elif isinstance(item, tuple): entries.append(FusionPattern(*item)) _REGISTERED_PATTERN_NAMES.add(item[0]) else: raise TypeError(f"Cannot register type {type(item)} as pattern") _ffi_api.RegisterPatterns(entries) def get_patterns_with_prefix(prefix: str) -> list[FusionPattern]: """ Get a list of patterns whose names startwith `prefix`. Parameters ---------- prefix: str The prefix of pattern name. Returns ------- patterns: FusionPattern Matched patterns, ordered by priority from high to low. """ return _ffi_api.GetPatternsWithPrefix(prefix) def get_pattern(name: str) -> FusionPattern | None: """ Find the pattern with a particular name. Parameters ---------- name: str The pattern name. Returns ------- pattern: Optional[FusionPattern] The matched pattern. Returns None if such pattern is not found. """ return _ffi_api.GetPattern(name)