859498dc01
## Summary This PR adds the initial TIRx support needed for low-level programming of Blackwell-class GPU architectures. As part of the ongoing TIRx refactor, it introduces TVMScript support for directly scripting advanced hardware features without relying on scheduling as the primary programming interface. The change keeps existing `s_tir` script support intact while making direct scripting a first-class path for TIRx programs. ## Main Changes - Add TIRx operator dispatch and layout infrastructure. - Add TVMScript support for new low-level TIRx operations. - Add analysis, transform, and lowering support for TIRx IR nodes. - Add CUDA/Blackwell-oriented codegen and intrinsic coverage. - Add Python and C++ integration points for TIRx scripting and runtime support. ## Validation - `pre-commit run --all-files` - `ninja -C build -j32` - `CUDA_VISIBLE_DEVICES=2 pytest tests/python/tirx/ -n 16` - `1723 passed, 47 skipped, 32 warnings` - `CUDA_VISIBLE_DEVICES=2 python -m pytest -v tests/python/all-platform-minimal-test` - `37 passed, 105 skipped` - `TVM_TEST_TARGETS=llvm python -m pytest -v tests/python/tirx-analysis tests/python/tirx-base tests/python/tirx-transform -n 16` - `664 passed, 25 skipped, 9 xfailed, 1 xpassed` ## Local CI Notes Some full CI-equivalent jobs were not locally reproducible because this machine is missing parts of the Apache TVM CI environment, including `llvm-config-15/17`, Vulkan, ROCm, Maven, Sphinx, Doxygen, Emscripten, and ARM/QEMU cross-toolchain components. Metal-specific tests were skipped locally because no Metal runtime is available.
210 lines
6.7 KiB
Python
210 lines
6.7 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.
|
|
"""The entry point of TVM parser."""
|
|
|
|
import inspect
|
|
from typing import Any
|
|
|
|
import tvm
|
|
|
|
from ....ir.module import IRModule
|
|
from ...ir_builder import IRBuilder
|
|
from . import doc
|
|
from .diagnostics import Source
|
|
from .error import ParserError
|
|
from .parser import Parser
|
|
|
|
WELL_FORMED_ERROR_MESSAGE = (
|
|
"Program is not well-formed. If this is deliberate, consider "
|
|
"setting check_well_formed in the top-level decorator to False "
|
|
"(e.g., @I.ir_module(check_well_formed=False) or "
|
|
"@R.function(check_well_formed=False))."
|
|
)
|
|
|
|
|
|
def _default_globals() -> dict[str, Any]:
|
|
# lazy import here to avoid circular deps
|
|
from tvm.script import tirx as _tirx_dsl # pylint: disable=import-outside-toplevel
|
|
from tvm.script.parser import (
|
|
ir, # pylint: disable=import-outside-toplevel
|
|
relax, # pylint: disable=import-outside-toplevel
|
|
)
|
|
from tvm.script.parser import tirx as _tirx_parser # pylint: disable=import-outside-toplevel
|
|
from tvm.tirx import layout as _tirx_layout # pylint: disable=import-outside-toplevel
|
|
|
|
# Expose the layout `Axis` class so printed layout sugar like
|
|
# `4 @ Axis.laneid` round-trips without per-script imports. Injecting just
|
|
# `Axis` (one short symbol) avoids name collisions with common user shape
|
|
# vars like `m`, `P`, `F` that registered axes happen to share names with.
|
|
return {
|
|
"tvm": tvm,
|
|
"I": ir,
|
|
"ir": ir,
|
|
"T": _tirx_parser,
|
|
"tir": _tirx_parser,
|
|
"R": relax,
|
|
"relax": relax,
|
|
"Tx": _tirx_dsl,
|
|
"tirx": _tirx_dsl,
|
|
"Axis": _tirx_layout.Axis,
|
|
}
|
|
|
|
|
|
def scan_macro(program: Any | str, extra_vars: dict[str, Any] | None = None) -> Any:
|
|
"""Generate the AST, and the source code for __repr__."""
|
|
# The AST will be converted into TIR at the time of expansion.
|
|
source = Source(program)
|
|
closure_vars = extra_vars or _default_globals()
|
|
return source, closure_vars
|
|
|
|
|
|
def parse(
|
|
program: doc.AST | Any | str,
|
|
extra_vars: dict[str, Any] | None = None,
|
|
check_well_formed: bool = True,
|
|
s_tir: bool = False,
|
|
) -> Any:
|
|
"""Register a method for a operand type, AST operator node and operand index.
|
|
|
|
Parameters
|
|
----------
|
|
program : Union[doc.AST, Any, str]
|
|
The TVMScript code to parse.
|
|
|
|
extra_vars : Dict[str, Any]
|
|
The extra variable table for parsing.
|
|
|
|
check_well_formed : bool
|
|
Whether to check well-formedness after parsing.
|
|
|
|
Returns
|
|
-------
|
|
func : Any
|
|
The parsed TVMScript program.
|
|
"""
|
|
if extra_vars is None:
|
|
extra_vars = _default_globals()
|
|
|
|
ann = {}
|
|
all_pyfuncs = {}
|
|
if inspect.isfunction(program):
|
|
ann = {program.__name__: program.__annotations__}
|
|
elif inspect.isclass(program):
|
|
for name, func in program.__dict__.items():
|
|
if inspect.isfunction(func):
|
|
ann[name] = func.__annotations__
|
|
all_pyfuncs[name] = func
|
|
|
|
source = Source(program)
|
|
parser = Parser(source, ann)
|
|
with IRBuilder() as builder:
|
|
try:
|
|
parser.parse(extra_vars=extra_vars)
|
|
except ParserError as err:
|
|
parser.report_error(err.node, err.args[0])
|
|
ret = builder.get()
|
|
# Attach pyfuncs to the IRModule
|
|
if inspect.isclass(program) and isinstance(ret, IRModule):
|
|
_attach_pyfuncs_to_irmodule(ret, all_pyfuncs)
|
|
|
|
# check well-formedness in both Relax and TIR
|
|
if check_well_formed:
|
|
check_ret = ret
|
|
if not isinstance(check_ret, IRModule):
|
|
check_ret = IRModule.from_expr(ret)
|
|
|
|
source_ast = source.as_ast()
|
|
|
|
if isinstance(ret, IRModule | tvm.relax.Function) and not tvm.relax.analysis.well_formed(
|
|
ret
|
|
):
|
|
parser.report_error(source_ast, err=WELL_FORMED_ERROR_MESSAGE)
|
|
|
|
try:
|
|
if s_tir:
|
|
tvm.tirx.analysis.verify_well_formed(check_ret)
|
|
else:
|
|
tvm.tirx.analysis.verify_tirx_well_formed(check_ret)
|
|
except Exception as err: # pylint: disable=broad-exception-caught
|
|
parser.report_error(
|
|
source_ast,
|
|
err=f"{WELL_FORMED_ERROR_MESSAGE}\n\nTraceback: {err!s}",
|
|
)
|
|
return ret
|
|
|
|
|
|
def _create_python_packed_func(pyfunc):
|
|
"""Create a PackedFunc wrapper for a Python function.
|
|
|
|
This function creates a PackedFunc that can be called from TVM runtime
|
|
and will execute the original Python function.
|
|
|
|
Parameters
|
|
----------
|
|
pyfunc : Callable
|
|
The Python function to wrap.
|
|
|
|
Returns
|
|
-------
|
|
PackedFunc
|
|
A PackedFunc that wraps the Python function.
|
|
"""
|
|
|
|
def packed_func_wrapper(*args, **kwargs):
|
|
"""Wrapper function that calls the original Python function."""
|
|
try:
|
|
result = pyfunc(*args, **kwargs)
|
|
return result
|
|
except Exception as error:
|
|
print(f"Error calling Python function {pyfunc.__name__}: {error}")
|
|
raise
|
|
|
|
return packed_func_wrapper
|
|
|
|
|
|
def _attach_pyfuncs_to_irmodule(irmodule, all_pyfuncs):
|
|
"""Attach Python functions to IRModule with reduced nesting."""
|
|
if not all_pyfuncs:
|
|
return
|
|
|
|
if not hasattr(irmodule, "pyfuncs"):
|
|
irmodule.pyfuncs = {}
|
|
|
|
for global_var, func in irmodule.functions_items():
|
|
if not isinstance(func, tvm.relax.ExternFunc):
|
|
continue
|
|
if not func.attrs.get("is_pyfunc", False):
|
|
continue
|
|
|
|
pyfunc_name = global_var.name_hint
|
|
if pyfunc_name not in all_pyfuncs:
|
|
continue
|
|
|
|
pyfunc = all_pyfuncs[pyfunc_name]
|
|
irmodule.pyfuncs[pyfunc_name] = pyfunc
|
|
|
|
try:
|
|
source_code = inspect.getsource(pyfunc)
|
|
func = func.with_attr("python_source", source_code)
|
|
except (OSError, TypeError):
|
|
func = func.with_attr("python_source", f"# Source unavailable for {pyfunc_name}")
|
|
|
|
packed_func = _create_python_packed_func(pyfunc)
|
|
func = func.with_attr("python_packed_func", packed_func)
|
|
|
|
irmodule[global_var] = func
|