This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 3a0b83a7e9 refactor(infra)!: pass backend configuration through Target
(#20651)
3a0b83a7e9 is described below
commit 3a0b83a7e966ddd35fff6bc811e4923f50cbfb1e
Author: Bohan Hou <[email protected]>
AuthorDate: Sun Oct 11 07:27:33 2026 -0400
refactor(infra)!: pass backend configuration through Target (#20651)
CUDA backend configuration already travels through Target attributes.
Remove the parallel `backend_config` keyword from `tvm.compile`,
`tvm.tirx.build`, `tvm.relax.build`, and `IketProfiler.compile`;
compilation now resolves its defaults from the supplied Target.
```python
import tvm
from tvm.backend.cuda import BackendConfig
target = tvm.target.Target({
"kind": "cuda",
"arch": "sm_100a",
"backend_config": {
"cuda": BackendConfig(compiler="nvrtc", nvrtc=["--use_fast_math",
"--ftz=false"])
},
})
executable = tvm.compile(func, target=target)
```
Each `device_entry(backend_config=...)` keeps its independent overrides,
including architecture and toolchain settings. The existing per-entry
resolution and module grouping continue to apply. Update callers, source
helpers, and documentation to use Target defaults followed by entry
overrides.
Follow-up to merged #20645.
Validation:
- Built successfully on the merged #20645 baseline (`0f861ee568`) in 67
seconds.
- Full TIRx suite: 2,689 passed / 290 skipped on seven B200 GPUs, 165
seconds.
- Backend configuration, launch, and IKET regressions: 131 passed / 2
skipped.
- CUDA codegen and Relax VM build regressions: 415 passed / 4 skipped.
- Updated TIRx-kernels callers: 94 focused tests passed; all eight MQA
registry correctness cases passed. Both workload and strict registry
import gates passed (274 workloads / 93 kernels).
- Changed-file and staged pre-commit checks passed.
The latest upstream commit (`27576ce2d1`) fails the local NCCL-enabled
C++ build in `cuda_ipc_memory.cc` after its object-handle changes. The
checks above therefore use the merged #20645 baseline; this follow-up
changes Python APIs, tests, and documentation only.
---
docs/tirx/api/cuda_compile.rst | 11 +++-
docs/tirx/native_basics/cuda/compiling.rst | 23 ++++---
python/tvm/backend/config.py | 32 ++++------
python/tvm/backend/cuda/iket.py | 6 +-
python/tvm/backend/cuda/transforms/__init__.py | 7 +--
python/tvm/driver/build_module.py | 8 +--
python/tvm/relax/vm_build.py | 15 +----
python/tvm/tirx/build.py | 13 ++--
tests/python/codegen/test_target_codegen_cuda.py | 79 ++++++++++++++----------
tests/python/disco/test_nvshmem.py | 3 +-
tests/python/tirx/codegen/test_codegen_cuda.py | 15 ++++-
tests/python/tirx/test_cuda_backend_config.py | 69 +++++++++++----------
tests/python/tirx/test_cuda_launch.py | 2 +-
13 files changed, 150 insertions(+), 133 deletions(-)
diff --git a/docs/tirx/api/cuda_compile.rst b/docs/tirx/api/cuda_compile.rst
index dc89fb615b..2f5c1d8e47 100644
--- a/docs/tirx/api/cuda_compile.rst
+++ b/docs/tirx/api/cuda_compile.rst
@@ -17,7 +17,8 @@
CUDA backend configuration
==========================
-Pass a nested mapping directly to ``tvm.compile(..., backend_config=...)`` or
+Set the nested mapping on ``Target.backend_config`` and pass that Target to
+``tvm.compile``. Individual kernels can override it with
``T.device_entry(..., backend_config=...)``. Each backend owns its
configuration;
CUDA currently provides these fixed keys:
@@ -58,10 +59,14 @@ compiler arguments are not modeled as separate Python
fields.
from tvm.backend.cuda import BackendConfig
cuda_config = BackendConfig(
- arch="sm_100a", compiler="nvrtc",
+ compiler="nvrtc",
nvrtc=["--use_fast_math", "--ftz=false"],
)
- executable = tvm.compile(func, backend_config={"cuda": cuda_config})
+ target = tvm.target.Target({
+ "kind": "cuda", "arch": "sm_100a",
+ "backend_config": {"cuda": cuda_config},
+ })
+ executable = tvm.compile(func, target=target)
.. autoclass:: tvm.backend.cuda.BackendConfig
:members:
diff --git a/docs/tirx/native_basics/cuda/compiling.rst
b/docs/tirx/native_basics/cuda/compiling.rst
index 68b56b3deb..7611b2a2fa 100644
--- a/docs/tirx/native_basics/cuda/compiling.rst
+++ b/docs/tirx/native_basics/cuda/compiling.rst
@@ -19,7 +19,8 @@ Compiling and inspecting
========================
CUDA compilation accepts a lightweight ``backend_config`` mapping. The same
-nested structure is accepted by ``tvm.compile``, ``T.device_entry``, and
+nested structure is accepted by the Target's ``backend_config`` attribute and
+``T.device_entry``. Pass the configured Target to ``tvm.compile`` or
``txl.Kernel.compile``. ``tvm.backend.cuda.BackendConfig`` is an optional
``TypedDict`` helper, also available as ``T.cuda.BackendConfig`` and
``txl.cuda.BackendConfig``:
@@ -46,8 +47,11 @@ nested structure is accepted by ``tvm.compile``,
``T.device_entry``, and
y = T.cuda.thread_idx("x")
A[y] = B[y] + T.float32(1)
- cuda_config = BackendConfig(arch="sm_100a")
- exe = tvm.compile(pipeline, backend_config={"cuda": cuda_config})
+ target = tvm.target.Target({
+ "kind": "cuda", "arch": "sm_100a",
+ "backend_config": {"cuda": BackendConfig(compiler="nvrtc")},
+ })
+ exe = tvm.compile(pipeline, target=target)
Each entry resolves its configuration before architecture-sensitive lowering.
Entries with different resolved targets or options compile as separate CUDA
@@ -58,8 +62,8 @@ Kernels must be compatible with the device on which they
execute.
Defaults and overrides
----------------------
-Precedence, from lowest to highest, is backend defaults, Target/tag defaults,
-compile-call overrides, then device-entry overrides. ``None`` and ``{}`` add no
+Precedence, from lowest to highest, is backend defaults, Target/tag settings,
+then device-entry overrides. ``None`` and ``{}`` add no
overrides. Toolchain lists replace inherited lists in full, and ``[]`` clears a
list. To keep fast math while changing FTZ, include both arguments as above.
Configuration boundaries snapshot dictionaries and lists, so later mutation of
@@ -79,11 +83,10 @@ Target tags use the existing target registry and carry
defaults in Target attrs:
Both ``Target("local/blackwell")`` and ``Target({"tag": "local/blackwell"})``
preserve these defaults in their expanded attributes. There is no separate
-backend tag registry. An explicit ``Target.arch`` conflicting with build-level
-``backend_config["cuda"]["arch"]`` is rejected. An entry can override the arch.
+backend tag registry. An entry can override the architecture supplied by its
Target.
Online builds can detect the GPU architecture. Offline builds require an
-explicit architecture in the Target, build configuration, or every device
entry.
+explicit architecture in the Target or every device entry.
A generic ``Target("cuda")`` may remain without an architecture for backend
discovery and IR construction; compilation validates it before lowering.
Architecture-dependent Python factories resolve and record their architecture
@@ -127,8 +130,8 @@ Migration
Replace the former compilation dataclass with the nested mapping above. Convert
math, debugging, include, and assembler options to native argument strings.
-Replace ``@txl.kernel(arch=...)`` and ``tirx.cuda_arch`` with an entry or
compile
-``backend_config={"cuda": {"arch": ...}}``.
+Replace ``@txl.kernel(arch=...)`` and ``tirx.cuda_arch`` with ``Target.arch``
or
+an entry's ``backend_config={"cuda": {"arch": ...}}``.
Compiler/math/ptxas environment policies remain removed. CPU benchmark
preparation receives an explicit architecture through ``backend_config`` and
diff --git a/python/tvm/backend/config.py b/python/tvm/backend/config.py
index 2d263bc471..8994d62d37 100644
--- a/python/tvm/backend/config.py
+++ b/python/tvm/backend/config.py
@@ -56,13 +56,12 @@ def parse_backend_config(value):
return copy_backend_config(json.loads(value) if value else None)
-def prepare_target(target, config=None, mod=None):
+def prepare_target(target, mod=None):
"""Resolve the build target, including architectures supplied by CUDA
entries."""
import tvm_ffi
from tvm.target import Target
- config = copy_backend_config(config)
active = Target.current() if target is None else target
if isinstance(active, Target) and active.kind.name == "cuda" and "arch"
not in active.attrs:
active = dict(active.export())
@@ -71,8 +70,13 @@ def prepare_target(target, config=None, mod=None):
or active == "cuda"
or (isinstance(active, Mapping) and active.get("kind") == "cuda" and
"arch" not in active)
)
+ if not generic_cuda:
+ return target
+ config = (
+ copy_backend_config(active.get("backend_config", {})) if
isinstance(active, Mapping) else {}
+ )
entries = []
- if mod is not None and generic_cuda:
+ if mod is not None:
from tvm.ir import RegionStmt
def visit(node):
@@ -83,23 +87,13 @@ def prepare_target(target, config=None, mod=None):
if not config and not any(entries):
return target
cuda = config.get("cuda", {})
- if generic_cuda:
- arch = cuda.get("arch")
- if arch is None and entries and all(c.get("cuda", {}).get("arch") for
c in entries):
- arch = entries[0]["cuda"]["arch"]
- attrs = dict(active) if isinstance(active, Mapping) else {"kind":
"cuda"}
- if arch is not None:
- attrs["arch"] = arch
- return Target(attrs)
- active = Target(active)
- if cuda and active.kind.name != "cuda":
- raise ValueError("CUDA backend_config requires a CUDA target")
arch = cuda.get("arch")
- if arch is not None and active.attrs.get("arch") != arch:
- raise ValueError(
- f"Target.arch={active.attrs.get('arch')!r} conflicts with
backend_config arch={arch!r}"
- )
- return active
+ if arch is None and entries and all(c.get("cuda", {}).get("arch") for c in
entries):
+ arch = entries[0]["cuda"]["arch"]
+ attrs = dict(active) if isinstance(active, Mapping) else {"kind": "cuda"}
+ if arch is not None:
+ attrs["arch"] = arch
+ return Target(attrs)
def argparse_backend_config(value):
diff --git a/python/tvm/backend/cuda/iket.py b/python/tvm/backend/cuda/iket.py
index ab148c0e20..3912adcb0d 100644
--- a/python/tvm/backend/cuda/iket.py
+++ b/python/tvm/backend/cuda/iket.py
@@ -309,16 +309,14 @@ class IketProfiler:
def sentinel_token(self, name: str):
return T.cuda.iket.sentinel_token(name)
- def compile(self, mod, target=None, *, tir_pipeline="tirx",
backend_config=None):
+ def compile(self, mod, target=None, *, tir_pipeline="tirx"):
"""Compile official IKET metadata and NativeDump placeholders."""
if isinstance(mod, tvm.tirx.Function):
mod = tvm.IRModule.from_expr(mod)
if not isinstance(mod, tvm.IRModule):
raise TypeError("IketProfiler.compile expects a TIRx Function or
IRModule")
enabled_mod = mod.with_attr("tirx.iket.enabled", True)
- executable = tvm.compile(
- enabled_mod, target=target, tir_pipeline=tir_pipeline,
backend_config=backend_config
- )
+ executable = tvm.compile(enabled_mod, target=target,
tir_pipeline=tir_pipeline)
return _OfficialIketExecutable(executable)
diff --git a/python/tvm/backend/cuda/transforms/__init__.py
b/python/tvm/backend/cuda/transforms/__init__.py
index 3859b03ddb..fd37f294cb 100644
--- a/python/tvm/backend/cuda/transforms/__init__.py
+++ b/python/tvm/backend/cuda/transforms/__init__.py
@@ -38,12 +38,9 @@ def LowerIket():
return get_global_func("tirx.backend.cuda.transforms.LowerIket")()
-def BindBackendConfig(backend_config=None):
+def BindBackendConfig():
"""Resolve per-entry compilation settings and targets before tile
dispatch."""
- from tvm.backend.config import backend_config_json
-
- config = "" if backend_config is None else
backend_config_json(backend_config)
- return
get_global_func("tirx.backend.cuda.transforms.BindBackendConfig")(config)
+ return
get_global_func("tirx.backend.cuda.transforms.BindBackendConfig")("")
def SpecializeDeviceHelpers():
diff --git a/python/tvm/driver/build_module.py
b/python/tvm/driver/build_module.py
index c1296e439f..8dceee2413 100644
--- a/python/tvm/driver/build_module.py
+++ b/python/tvm/driver/build_module.py
@@ -42,7 +42,6 @@ def compile( # pylint: disable=redefined-builtin
*,
relax_pipeline: tvm.transform.Pass | Callable | str | None = "default",
tir_pipeline: tvm.transform.Pass | Callable | str | None = "default",
- backend_config=None,
) -> Executable:
"""
Compile an IRModule to a runtime executable.
@@ -56,14 +55,12 @@ def compile( # pylint: disable=redefined-builtin
The input module to be compiled. Can be a Function or an IRModule
containing
TIR or Relax functions.
target : Optional[Target]
- The target platform to compile for.
+ The target platform and its backend compilation configuration.
relax_pipeline : Optional[Union[tvm.transform.Pass, Callable, str]]
The compilation pipeline to use for Relax functions.
Only used if the module contains Relax functions.
tir_pipeline : Optional[Union[tvm.transform.Pass, Callable, str]]
The compilation pipeline to use for TIR functions.
- backend_config : Optional[dict[str, dict]]
- Per-backend compiler defaults, overridden by each device entry.
Returns
-------
@@ -77,7 +74,6 @@ def compile( # pylint: disable=redefined-builtin
target,
relax_pipeline=relax_pipeline,
tir_pipeline=tir_pipeline,
- backend_config=backend_config,
)
- lib = tvm.tirx.build(mod, target, pipeline=tir_pipeline,
backend_config=backend_config)
+ lib = tvm.tirx.build(mod, target, pipeline=tir_pipeline)
return Executable(lib)
diff --git a/python/tvm/relax/vm_build.py b/python/tvm/relax/vm_build.py
index bd2bfc60ae..db65ecbf68 100644
--- a/python/tvm/relax/vm_build.py
+++ b/python/tvm/relax/vm_build.py
@@ -103,7 +103,6 @@ def _vmlink(
params: dict[str, list] | None = None,
*,
system_lib: bool | None = None,
- backend_config=None,
):
"""
Internal codegen function to make executable.
@@ -147,9 +146,7 @@ def _vmlink(
tir_ext_libs = []
if tir_mod is not None and len(tir_mod.get_global_vars()) > 0:
tir_mod = _auto_attach_system_lib_prefix(tir_mod, target, system_lib)
- lib = tvm.tirx.build(
- tir_mod, target=target, pipeline=tir_pipeline,
backend_config=backend_config
- )
+ lib = tvm.tirx.build(tir_mod, target=target, pipeline=tir_pipeline)
for ext_mod in ext_libs:
if _is_device_module(ext_mod):
tir_ext_libs.append(ext_mod)
@@ -172,7 +169,6 @@ def build(
tir_pipeline: None | str | tvm.transform.Pass = "default",
*,
system_lib: bool | None = None,
- backend_config=None,
) -> Executable:
"""
Build an IRModule to VM executable.
@@ -206,9 +202,6 @@ def build(
auto registers generated functions to the system.
By default auto detects based on the target.
- backend_config : Optional[dict[str, dict]]
- CUDA compiler defaults forwarded to the generated TIRx functions.
-
Returns
-------
ex: tvm.relax.Executable
@@ -236,10 +229,9 @@ def build(
constants = attrs.get("const_name_to_constant", {})
return ext_libs, constants
- from tvm.backend.config import copy_backend_config, prepare_target
+ from tvm.backend.config import prepare_target
- backend_config = copy_backend_config(backend_config)
- target = prepare_target(target, backend_config, mod)
+ target = prepare_target(target, mod)
if isinstance(target, str):
target = tvm.target.Target(target)
if not params:
@@ -280,7 +272,6 @@ def build(
ext_libs=ext_libs,
params=params,
system_lib=system_lib,
- backend_config=backend_config,
)
diff --git a/python/tvm/tirx/build.py b/python/tvm/tirx/build.py
index 31b9d17c36..8e65df8cbf 100644
--- a/python/tvm/tirx/build.py
+++ b/python/tvm/tirx/build.py
@@ -163,8 +163,6 @@ def build(
mod: Function | IRModule,
target: str | Target | None = None,
pipeline: str | tvm.transform.Pass | None = "default",
- *,
- backend_config=None,
):
"""Build a function with a signature, generating code for devices
coupled with target information.
@@ -174,11 +172,9 @@ def build(
mod : Union[Function, IRModule]
The input to be built.
target : Optional[Union[str, Target]]
- The target for compilation.
+ The target for compilation, including backend configuration defaults.
pipeline : Union[None, str, tvm.transform.Pass]
The pipeline to use for compilation.
- backend_config : Optional[dict[str, dict]]
- Per-backend compiler defaults, overridden by each device entry.
Returns
-------
@@ -191,11 +187,10 @@ def build(
else:
assert isinstance(mod, tvm.IRModule)
- from tvm.backend.config import copy_backend_config, prepare_target
+ from tvm.backend.config import prepare_target
from tvm.backend.cuda.transforms import BindBackendConfig,
SpecializeEntryHelpers
- backend_config = copy_backend_config(backend_config)
- target = prepare_target(target, backend_config, mod)
+ target = prepare_target(target, mod)
# Step 0: Determine the target in environment
# It's used to bind the Function without target attr to serve as a default
target
@@ -228,7 +223,7 @@ def build(
# Step 3: Bind the target to the input module
mod = tvm.tirx.transform.BindTarget(target_to_bind)(mod)
- mod = BindBackendConfig(backend_config)(mod)
+ mod = BindBackendConfig()(mod)
mod = SpecializeEntryHelpers()(mod)
# Step 4: Apply the tirx pipeline
diff --git a/tests/python/codegen/test_target_codegen_cuda.py
b/tests/python/codegen/test_target_codegen_cuda.py
index 3a8181e5b9..31b23b0176 100644
--- a/tests/python/codegen/test_target_codegen_cuda.py
+++ b/tests/python/codegen/test_target_codegen_cuda.py
@@ -55,7 +55,9 @@ def test_cuda_host_bundle(tmp_path, *, backend_config):
B[tx] = A[tx] + T.float32(1)
target = tvm.target.Target("cuda", host="cuda_host")
- built = tvm.compile(add_one, target=target,
backend_config=backend_config).mod
+ built = tvm.compile(
+ add_one, target={**tvm.target.Target(target).export(),
"backend_config": backend_config}
+ ).mod
source = export_cuda_host(built)
assert "tvm_cuda_binary_0" in source
assert "__global__" not in source
@@ -116,9 +118,8 @@ def test_cuda_host_bundle_bf16_cluster(tmp_path, *,
backend_config):
with target:
built = tvm.compile(
tvm.IRModule({"main": main}),
- target=target,
+ target={**tvm.target.Target(target).export(), "backend_config":
backend_config},
tir_pipeline="tirx",
- backend_config=backend_config,
).mod
source = export_cuda_host(built)
assert "cudaLaunchAttributeClusterDimension" in source
@@ -169,7 +170,9 @@ def
test_cuda_host_bundle_programmatic_dependent_launch(tmp_path, *, backend_con
target = tvm.target.Target("cuda", host="cuda_host")
source = export_cuda_host(
- tvm.compile(add_one, target=target, backend_config=backend_config).mod
+ tvm.compile(
+ add_one, target={**tvm.target.Target(target).export(),
"backend_config": backend_config}
+ ).mod
)
assert "cudaLaunchAttributeProgrammaticStreamSerialization" in source
library = tvm_ffi.cpp.build_inline(
@@ -217,9 +220,8 @@ def test_cuda_host_bundle_tensor_map_parameter(tmp_path,
monkeypatch, *, backend
with target:
built = tvm.compile(
tvm.IRModule({"main": main}),
- target=target,
+ target={**tvm.target.Target(target).export(), "backend_config":
backend_config},
tir_pipeline="tirx",
- backend_config=backend_config,
).mod
source = export_cuda_host(built)
assert "(CUtensorMap*)" in source
@@ -259,7 +261,7 @@ def test_cuda_vectorize_add(*, backend_config):
if i_0 * num_thread + i_1 < n:
B[i_0 * num_thread + i_1] = A[i_0 * num_thread +
i_1] + one
- fun = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -323,7 +325,7 @@ def test_cuda_bf16_vectorize_add(*, backend_config):
with tvm.transform.PassContext(
disabled_pass=["tirx.BF16Promote", "tirx.BF16CastElimination",
"tirx.BF16TypeLowering"]
):
- fun = tvm.compile(Module, target="cuda",
backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda",
"backend_config": backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -375,7 +377,7 @@ def test_cuda_multiply_add(*, backend_config):
ty="int32",
)
- fun = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
np_a = np.random.randint(low=-128, high=127, size=(n, lanes))
np_b = np.random.randint(low=-128, high=127, size=(n, lanes))
@@ -414,7 +416,7 @@ def test_cuda_vectorize_load(*, backend_config):
for i_1 in T.thread_binding(num_thread,
thread="threadIdx.x"):
B[i_0 * num_thread + i_1] = A[i_0 * num_thread + i_1]
- fun = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -449,7 +451,7 @@ def test_cuda_make_int8(*, backend_config):
for j in T.vectorized(lanes):
A[i, j] = const_value
- fun = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -487,7 +489,7 @@ def test_cuda_inf_nan(*, backend_config):
if i_0 * 8 + i_1 < n:
C[i_0 * 8 + i_1] = inf_value
- fun = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -546,7 +548,10 @@ def test_crossthread_reduction1(target, *, backend_config):
B[i] = reduced[0]
fun = tvm.compile(
- Module, target=target, backend_config=backend_config if target ==
"cuda" else None
+ Module,
+ target={**tvm.target.Target(target).export(), "backend_config":
backend_config}
+ if target == "cuda"
+ else target,
)
return fun
@@ -627,7 +632,10 @@ def test_crossthread_reduction2(target, *, backend_config):
B[i] = reduced[0]
func = tvm.compile(
- Module, target=target, backend_config=backend_config if target ==
"cuda" else None
+ Module,
+ target={**tvm.target.Target(target).export(), "backend_config":
backend_config}
+ if target == "cuda"
+ else target,
)
return func
@@ -670,7 +678,7 @@ def test_cuda_reduction_binding(*, backend_config):
B[m_0 * 32 + m_1] = T.float32(0.0)
B[m_0 * 32 + m_1] = B[m_0 * 32 + m_1] + A[m_0 * 32 +
m_1, k]
- func = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ func = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
@pytest.mark.gpu
@@ -702,7 +710,7 @@ def test_cuda_const_float_to_half(*, backend_config):
]
)
- func = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ func = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
shape = (2, 3, 4)
a_np = np.random.uniform(size=shape).astype("float16")
@@ -738,7 +746,7 @@ def test_cuda_floordiv_with_vectorization(*,
backend_config):
(i_0 * 256 + i_1_0 * 4 + i_1_1) // 37
]
- func = tvm.compile(Module, target="cuda",
backend_config=backend_config)
+ func = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -772,7 +780,7 @@ def test_cuda_floormod_with_vectorization(*,
backend_config):
(i_0 * 256 + i_1_0 * 4 + i_1_1) % 37
]
- func = tvm.compile(Module, target="cuda",
backend_config=backend_config)
+ func = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
def run_and_check():
dev = tvm.cuda(0)
@@ -855,7 +863,7 @@ def test_vectorized_casts(t0, t1, factor, *,
backend_config):
t0, B[i_0 * factor + i_1]
)
- func = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ func = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
# correctness
def run_and_check():
@@ -894,7 +902,7 @@ def sched(compute_fn, dtype, n=128, *, backend_config=None):
A[i0_1_0 * 4 + i0_1_1_0 * 4 + i0_1_1_1]
)
- return tvm.compile(Module, target="cuda", backend_config=backend_config)
+ return tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
@pytest.mark.gpu
@@ -1063,7 +1071,7 @@ def test_cuda_vectorize_load_permute_pad(*,
backend_config):
j < padding or l + padding <= j, zero, A[i *
lanes + k, j - padding]
)
- fun = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ fun = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
np_a = np.random.randint(low=-128, high=127, size=(n, l)).astype(dtype)
np_a_reshape = np_a.reshape(n // lanes, lanes, l).transpose(0, 2, 1)
@@ -1102,7 +1110,7 @@ def test_try_unaligned_vector_load(*, backend_config):
for i_1 in T.vectorized(2):
C[i_0 * 2 + i_1] = A[i_0 * 2 + i_1 + offset]
- f = tvm.tirx.build(Module, target="cuda",
backend_config=backend_config)
+ f = tvm.tirx.build(Module, target={"kind": "cuda", "backend_config":
backend_config})
kernel_source = f.imports[0].inspect_source()
a_data = np.arange(0, N).astype("float16")
@@ -1163,7 +1171,7 @@ def test_cuda_thread_sync_inside_condition(*,
backend_config):
for func in [func2, func3]:
mod = tvm.IRModule({"main": func})
- compiled = tvm.compile(mod, target="cuda",
backend_config=backend_config)
+ compiled = tvm.compile(mod, target={"kind": "cuda", "backend_config":
backend_config})
assert "__syncthreads()" in compiled.mod.imports[0].inspect_source()
@@ -1176,7 +1184,7 @@ def test_invalid_reinterpret(*, backend_config):
B[tx] = T.call_intrin("tirx.reinterpret", A[tx], ty="uint8")
with pytest.raises(RuntimeError):
- tvm.compile(func, target="cuda", backend_config=backend_config)
+ tvm.compile(func, target={"kind": "cuda", "backend_config":
backend_config})
@pytest.mark.gpu
@@ -1197,7 +1205,7 @@ def test_cuda_tensormap(*, backend_config):
# fmt: on
mod = tvm.IRModule({"main": main})
- mod = tvm.compile(mod, target="cuda", backend_config=backend_config)
+ mod = tvm.compile(mod, target={"kind": "cuda", "backend_config":
backend_config})
assert (
"""
extern "C" __global__ void __launch_bounds__(128) main_kernel(float*
__restrict__ A_ptr, const __grid_constant__ CUtensorMap A_map) {
@@ -1228,7 +1236,7 @@ def test_cuda_device_func_call(*, backend_config):
for tx in T.thread_binding(1024, "threadIdx.x"):
C[bx, tx] = Module.add(A[bx, tx], B[bx, tx])
- lib = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ lib = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
cuda_code = lib.mod.imports[0].inspect_source()
assert 'extern "C" __device__ float add(float a, float b) {\n return (a +
b);\n}' in cuda_code
@@ -1248,7 +1256,7 @@ def test_cuda_float_const_hex_format(*, backend_config):
for tx in T.thread_binding(1024, "threadIdx.x"):
A[bx, tx] = T.float32(1 / 27)
- lib = tvm.compile(Module, target="cuda", backend_config=backend_config)
+ lib = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
cuda_code = lib.mod.imports[0].inspect_source()
assert "0x1.2f684bda12f68p-5f" in cuda_code
@@ -1279,7 +1287,9 @@ def test_device_host_call_same_func(*, backend_config):
# 2. We set a dummy mcpu value for testing purpose,
# in order to avoid checking a function is host or device based on the
"cpu" substring.
target = tvm.target.Target({"kind": "cuda", "mcpu": "dummy_mcpu"},
host="c")
- lib = tvm.compile(Module, target=target, backend_config=backend_config)
+ lib = tvm.compile(
+ Module, target={**tvm.target.Target(target).export(),
"backend_config": backend_config}
+ )
cuda_code = lib.mod.imports[0].inspect_source()
assert 'extern "C" __device__ int add(int a, int b) {\n return (a +
b);\n}' in cuda_code
@@ -1313,7 +1323,11 @@ def test_thread_return(monkeypatch, *, backend_config):
B[bx, tx] = A[bx, tx]
lib = tvm.compile(
- Module, backend_config=merge_backend_configs(backend_config, {"cuda":
{"arch": "sm_80"}})
+ Module,
+ target={
+ "kind": "cuda",
+ "backend_config": merge_backend_configs(backend_config, {"cuda":
{"arch": "sm_80"}}),
+ },
)
cuda_code = lib.mod.imports[0].inspect_source()
assert "return;" in cuda_code
@@ -1336,7 +1350,10 @@ def test_cuda_loop_step(*, backend_config):
C[i] = A[i] + B[i]
target = tvm.target.Target({"kind": "cuda"})
- lib = tvm.compile(cuda_loop_step, target=target,
backend_config=backend_config)
+ lib = tvm.compile(
+ cuda_loop_step,
+ target={**tvm.target.Target(target).export(), "backend_config":
backend_config},
+ )
cuda_src = lib.mod.imports[0].inspect_source()
assert "i += 96" in cuda_src
@@ -1371,7 +1388,7 @@ def test_export_load_with_fallback(monkeypatch, tmp_path,
*, backend_config):
B[i_0 * 32 + i_1] = A[i_0 * 32 + i_1] + 1.0
monkeypatch.setenv("TVM_COMPILE_FORCE_FALLBACK", "1")
- host_lib = tvm.compile(Module, target="cuda",
backend_config=backend_config)
+ host_lib = tvm.compile(Module, target={"kind": "cuda", "backend_config":
backend_config})
monkeypatch.delenv("TVM_COMPILE_FORCE_FALLBACK")
lib_path = str(tmp_path / "lib.so")
diff --git a/tests/python/disco/test_nvshmem.py
b/tests/python/disco/test_nvshmem.py
index 771701d4ac..2e6f7b183e 100644
--- a/tests/python/disco/test_nvshmem.py
+++ b/tests/python/disco/test_nvshmem.py
@@ -339,7 +339,8 @@ def _kernel_compile(compile_mode):
target = tvm.target.Target("cuda")
tvm.compile(
- NvshmemQueryModule, target=target,
backend_config=backend_config
+ NvshmemQueryModule,
+ target={**tvm.target.Target(target).export(),
"backend_config": backend_config},
).export_library(path)
mod = sess.load_vm_module(path)
result = mod["main"]()
diff --git a/tests/python/tirx/codegen/test_codegen_cuda.py
b/tests/python/tirx/codegen/test_codegen_cuda.py
index 1bb4f79fab..64f23928e3 100644
--- a/tests/python/tirx/codegen/test_codegen_cuda.py
+++ b/tests/python/tirx/codegen/test_codegen_cuda.py
@@ -23,6 +23,7 @@ import pytest
import tvm
import tvm.testing
+from tvm.backend.config import merge_backend_configs
from tvm.backend.cuda.lang.clc import query_cancel_first_ctaid_x
from tvm.script import tirx as T
from tvm.testing import env
@@ -34,9 +35,21 @@ def _get_source(
if target is None:
target = {"kind": "cuda", "arch": "sm_100a"}
target = tvm.target.Target(target)
+ target = tvm.target.Target(
+ {
+ **target.export(),
+ "backend_config": merge_backend_configs(
+ target.attrs.get("backend_config"), backend_config
+ ),
+ }
+ )
mod = tvm.IRModule({"main": func})
with target:
- mod = tvm.compile(mod, target=target, tir_pipeline="tirx",
backend_config=backend_config)
+ mod = tvm.compile(
+ mod,
+ target=target,
+ tir_pipeline="tirx",
+ )
src = mod.mod.imports[0].inspect_source()
return src, mod
diff --git a/tests/python/tirx/test_cuda_backend_config.py
b/tests/python/tirx/test_cuda_backend_config.py
index 5dd2ed765e..a1de86d7a2 100644
--- a/tests/python/tirx/test_cuda_backend_config.py
+++ b/tests/python/tirx/test_cuda_backend_config.py
@@ -192,17 +192,21 @@ def test_entry_ir_roundtrip_and_resolution():
kernel, tvm.script.from_source(kernel.script(), extra_vars={"T": T})
)
tvm.ir.assert_structural_equal(kernel,
tvm.ir.load_json(tvm.ir.save_json(kernel)))
- target = tvm.target.Target({"kind": "cuda", "arch": "sm_100a"},
host="llvm")
- mod = tvm.tirx.transform.BindTarget(target)(tvm.IRModule({"main": kernel}))
- mod = BindBackendConfig(
+ target = tvm.target.Target(
{
- "cuda": {
- "arch": "sm_100a",
- "nvcc": ["--use_fast_math", "--generate-line-info"],
- "nvrtc": ["--use_fast_math", "--generate-line-info"],
- }
- }
- )(mod)
+ "kind": "cuda",
+ "arch": "sm_100a",
+ "backend_config": {
+ "cuda": {
+ "nvcc": ["--use_fast_math", "--generate-line-info"],
+ "nvrtc": ["--use_fast_math", "--generate-line-info"],
+ }
+ },
+ },
+ host="llvm",
+ )
+ mod = tvm.tirx.transform.BindTarget(target)(tvm.IRModule({"main": kernel}))
+ mod = BindBackendConfig()(mod)
entries = []
tvm_ffi.structural_walk(
mod,
@@ -220,14 +224,6 @@ def test_entry_ir_roundtrip_and_resolution():
assert "--generate-line-info" not in config["cuda"]["nvrtc"]
-def test_target_arch_conflict():
- from tvm.backend.config import prepare_target
-
- with pytest.raises(ValueError, match="conflicts"):
- prepare_target({"kind": "cuda", "arch": "sm_90"}, {"cuda": {"arch":
"sm_100a"}})
- assert prepare_target(None, {"cuda": {"arch": "sm_100a"}}).arch ==
"sm_100a"
-
-
@pytest.mark.gpu
@pytest.mark.skipif(not tvm.cuda().exist, reason="requires CUDA")
@pytest.mark.parametrize("compiler", ["nvrtc", "nvcc"])
@@ -247,13 +243,16 @@ def test_backend_config_controls_actual_ftz(compiler):
for ftz in (False, True):
module = tvm.compile(
kernel,
- backend_config={
- "cuda": {
- "arch": arch,
- "compiler": compiler,
- "nvcc": ["--use_fast_math", "--ftz" + "=" +
str(ftz).lower()],
- "nvrtc": ["--use_fast_math", "--ftz" + "=" +
str(ftz).lower()],
- }
+ target={
+ "kind": "cuda",
+ "backend_config": {
+ "cuda": {
+ "arch": arch,
+ "compiler": compiler,
+ "nvcc": ["--use_fast_math", "--ftz" + "=" +
str(ftz).lower()],
+ "nvrtc": ["--use_fast_math", "--ftz" + "=" +
str(ftz).lower()],
+ }
+ },
},
)
b = tvm.runtime.empty((32,), "float32", tvm.cuda())
@@ -302,7 +301,9 @@ def test_two_entries_keep_compilers_math_and_order():
C[y] = B[y] * T.float32(0.5)
arch = env.cuda_arch(0)
- module = tvm.compile(kernel, backend_config={"cuda": {"arch": arch}})
+ module = tvm.compile(
+ kernel, target={"kind": "cuda", "backend_config": {"cuda": {"arch":
arch}}}
+ )
assert len(module.mod.imports) == 2
configs = [parse_backend_config(m.inspect_source("backend_config")) for m
in module.mod.imports]
assert {c["cuda"]["compiler"] for c in configs} == {"nvcc", "nvrtc"}
@@ -365,7 +366,7 @@ def
test_serialized_config_replays_in_fresh_process(tmp_path, monkeypatch, fallb
"nvrtc": ["--use_fast_math", "--ftz=false",
"--generate-line-info"],
}
}
- module = tvm.compile(main, backend_config=config)
+ module = tvm.compile(main, target={"kind": "cuda", "backend_config":
config})
library = str(tmp_path / "replay.so")
module.export_library(library)
script = """
@@ -612,7 +613,9 @@ from tvm.script import tirx as T
target = tvm.target.Target("cuda", host="llvm")
assert "arch" not in target.attrs
assert not tvm.testing.device_enabled("cuda")
-resolved = prepare_target(target, {"cuda": BackendConfig(arch="sm_100a")})
+resolved = prepare_target({
+ **target.export(), "backend_config": {"cuda":
BackendConfig(arch="sm_100a")}
+})
assert resolved.arch == "sm_100a"
assert resolved.host.kind.name == "llvm"
@@ -685,7 +688,9 @@ def
test_source_fallback_does_not_invoke_compiler(monkeypatch):
T.device_entry(launch=T.cuda.LaunchConfig(grid=1, block=32))
A[T.cuda.thread_idx("x")] = T.float32(1)
- module = tvm.compile(main, backend_config={"cuda": {"arch": "sm_100a"}})
+ module = tvm.compile(
+ main, target={"kind": "cuda", "backend_config": {"cuda": {"arch":
"sm_100a"}}}
+ )
assert "__global__" in module.mod.imports[0].inspect_source("cuda")
assert (
parse_backend_config(module.mod.imports[0].inspect_source("backend_config"))["cuda"]["arch"]
@@ -705,7 +710,9 @@ def test_identical_entries_share_one_module(monkeypatch):
A[T.cuda.thread_idx("x")] = T.float32(2)
config["cuda"]["nvrtc"].clear()
- module = tvm.compile(main, backend_config={"cuda": {"arch": "sm_100a"}})
+ module = tvm.compile(
+ main, target={"kind": "cuda", "backend_config": {"cuda": {"arch":
"sm_100a"}}}
+ )
assert len(module.mod.imports) == 1
resolved =
parse_backend_config(module.mod.imports[0].inspect_source("backend_config"))
assert resolved["cuda"]["nvrtc"] == ["--ftz=false"]
@@ -726,7 +733,7 @@ def test_build_snapshots_config_before_passes(monkeypatch):
config["cuda"]["nvrtc"] = ["--ftz=true"]
with tvm.transform.PassContext(instruments=[ChangeCallerConfig()]):
- module = tvm.compile(main, backend_config=config)
+ module = tvm.compile(main, target={"kind": "cuda", "backend_config":
config})
resolved =
parse_backend_config(module.mod.imports[0].inspect_source("backend_config"))
assert config["cuda"]["nvrtc"] == ["--ftz=true"]
assert resolved["cuda"]["nvrtc"] == ["--ftz=false"]
diff --git a/tests/python/tirx/test_cuda_launch.py
b/tests/python/tirx/test_cuda_launch.py
index 4eda010109..70bbc25236 100644
--- a/tests/python/tirx/test_cuda_launch.py
+++ b/tests/python/tirx/test_cuda_launch.py
@@ -327,4 +327,4 @@ def
test_static_shared_memory_override_cannot_shrink_allocation():
scratch[0] = T.cuda.thread_idx("x")
with pytest.raises(ValueError, match="smaller than"):
- tvm.compile(kernel, backend_config={"cuda": {"arch": "sm_80"}})
+ tvm.compile(kernel, target={"kind": "cuda", "backend_config": {"cuda":
{"arch": "sm_80"}}})