Files
Bohan Hou 859498dc01 [TIRx] Bringup TIRx Infrastructure (#19581)
## 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.
2026-05-18 16:44:43 -07:00

245 lines
9.1 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.
"""Target data structure."""
import tvm_ffi
from tvm_ffi import Map
from tvm_ffi.core import String
from tvm.runtime import Device, Object, convert
from . import _ffi_api
@tvm_ffi.register_object("target.TargetKind")
class TargetKind(Object):
"""Kind of a compilation target"""
@property
def options(self):
"""Returns the dict of available option names and types"""
return dict(_ffi_api.ListTargetKindOptions(self))
@staticmethod
def options_from_name(kind_name: str):
"""Returns the dict of available option names and types from a name of TargetKind"""
return dict(_ffi_api.ListTargetKindOptionsFromName(kind_name))
class TargetFeatures:
def __init__(self, target):
self.target = target
def __getattr__(self, name: str):
return _ffi_api.TargetGetFeature(self.target, name)
@tvm_ffi.register_object("target.Target")
class Target(Object):
"""Target device information, use through TVM API.
Targets can be constructed from:
- A JSON config dictionary: ``Target({"kind": "cuda", "arch": "sm_80"})``
- A tag name: ``Target("nvidia/nvidia-a100")``
- A tag with overrides: ``Target({"tag": "nvidia/nvidia-a100", "l2_cache_size_bytes": 12345})``
- A kind name: ``Target("cuda")``
Use ``target.attrs["key"]`` to access target attributes.
Examples
--------
.. code-block:: python
# From a tag
target = Target("nvidia/nvidia-a100")
# From a tag with attribute overrides
target = Target({"tag": "qcom/hexagon-v68", "vtcm-capacity": 70000})
# From a config dictionary
target = Target({"kind": "cuda", "arch": "sm_80"})
"""
def __init__(self, target, host=None):
"""Construct a TVM target object from
1) Raw target string
2) Target config dict
3) Target tag string
4) Tag with overrides dict
Parameters
----------
target : Union[str, Dict[str, Any]]
Can be one of a literal target string, a json string describing
a configuration, or a dictionary of configuration options.
When using a dictionary or json string to configure target, the
possible values are:
tag : str (optional)
A registered tag name (e.g. ``"nvidia/nvidia-a100"``).
When ``tag`` is present, the tag's base config is loaded and
any additional fields in the dict override the base values.
The ``kind`` field is not needed when ``tag`` is specified.
kind : str (required unless tag is specified)
Which codegen path to use, for example 'llvm' or 'cuda'.
keys : List of str (optional)
A set of strategies that can be dispatched to. When using
"kind=opencl" for example, one could set keys to ["mali", "opencl", "gpu"].
device : str (optional)
A single key that corresponds to the actual device being run on.
This will be effectively appended to the keys.
libs : List of str (optional)
The set of external libraries to use. For example ['cblas', 'mkl'].
system-lib : bool (optional)
If True, build a module that contains self registered functions.
Useful for environments where dynamic loading like dlopen is banned.
mcpu : str (optional)
The specific cpu being run on. Serves only as an annotation.
model : str (optional)
An annotation indicating what model a workload came from.
runtime : str (optional)
An annotation indicating which runtime to use with a workload.
mtriple : str (optional)
The llvm triplet describing the target, for example "arm64-linux-android".
mattr : List of str (optional)
The llvm features to compile with, for example ["+avx512f", "+mmx"].
mfloat-abi : str (optional)
An llvm setting that is one of 'hard' or 'soft' indicating whether to use
hardware or software floating-point operations.
mabi : str (optional)
An llvm setting. Generate code for the specified ABI, for example "lp64d".
host : Union[str, Dict[str, Any]] (optional)
Description for target host. Can be recursive. Similar to target.
host : Optional[Union[str, Dict[str, Any]]]
Similar to target but for target host. Can be one of a literal target host string,
a json string describing a configuration, or a dictionary of configuration options.
When using a dictionary or json string to configure target, the possible values are
same as target.
"""
if isinstance(target, dict | str):
target = convert(target)
if isinstance(host, dict | str):
host = convert(host)
if target is None or not isinstance(target, Map | String | Target | str):
raise ValueError(f"target has to be a string or dictionary. instead get {type(target)}")
if host is not None:
if not isinstance(host, Map | String | Target | str):
raise ValueError("target host has to be a string or dictionary.")
self.__init_handle_by_constructor__(_ffi_api.Target, Target(target), Target(host))
else:
self.__init_handle_by_constructor__(_ffi_api.Target, target)
def __enter__(self):
_ffi_api.TargetEnterScope(self)
return self
def __exit__(self, ptype, value, trace):
_ffi_api.TargetExitScope(self)
def export(self):
return _ffi_api.TargetExport(self)
def with_host(self, host=None):
return _ffi_api.WithHost(self, Target(host))
@staticmethod
def from_device(device: str | Device) -> "Target":
"""Detects Target associated with the given device. If the device does not exist,
there will be an Error.
Parameters
----------
dev : Union[str, Device]
The device to detect the target for.
Supported device types: ["cuda", "metal", "rocm", "vulkan", "opencl", "cpu"]
Returns
-------
target : Target
The detected target.
"""
from .detect_target import ( # pylint: disable=import-outside-toplevel
detect_target_from_device,
)
return detect_target_from_device(device)
@staticmethod
def current(allow_none=True):
"""Returns the current target.
Parameters
----------
allow_none : bool
Whether allow the current target to be none
Raises
------
ValueError if current target is not set.
"""
return _ffi_api.TargetCurrent(allow_none)
@property
def features(self):
return TargetFeatures(self)
def __getattr__(self, name: str):
"""Backward-compatible attribute access for target attrs.
Historically, code accessed target options via attribute syntax
(e.g. ``target.arch``). Newer APIs prefer ``target.attrs["arch"]``.
"""
attrs = self.attrs
if name in attrs:
value = attrs[name]
return str(value) if isinstance(value, String) else value
raise AttributeError(f"'Target' object has no attribute '{name}'")
def get_kind_attr(self, attr_name):
"""Get additional attribute about the target kind.
Parameters
----------
attr_name : str
The attribute name.
Returns
-------
value : object
The attribute value
"""
return _ffi_api.TargetKindGetAttr(self.kind, attr_name)
def get_target_device_type(self):
"""Returns the device_type for this target."""
return _ffi_api.TargetGetDeviceType(self)
@staticmethod
def list_kinds():
"""Returns the list of available target names."""
return list(_ffi_api.ListTargetKinds())
@staticmethod
def target_or_current(target):
"""Returns target, or the current target in the environment if target is None"""
if target is None:
target = Target.current()
if target is None:
raise ValueError("Target is not set in env or passed as argument.")
return target