thisistaimur commited on
Commit
aec59ae
·
verified ·
1 Parent(s): d9c52fb

Uploaded using `kernel-builder`.

Browse files
build/torch-cuda/__init__.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Public package interface for tiledattention."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ try:
8
+ from ._errors import (
9
+ DependencyError,
10
+ DTypeNotSupportedError,
11
+ InvalidShapeError,
12
+ TiledAttentionError,
13
+ UnsupportedPlatformError,
14
+ )
15
+ from ._version import __version__
16
+ except ImportError: # pragma: no cover - flattened kernel package layout
17
+ from _errors import ( # type: ignore
18
+ DependencyError,
19
+ DTypeNotSupportedError,
20
+ InvalidShapeError,
21
+ TiledAttentionError,
22
+ UnsupportedPlatformError,
23
+ )
24
+ from _version import __version__ # type: ignore
25
+
26
+ if TYPE_CHECKING:
27
+ import torch
28
+
29
+
30
+ def sdpa(
31
+ q: torch.Tensor,
32
+ k: torch.Tensor,
33
+ v: torch.Tensor,
34
+ *,
35
+ causal: bool = False,
36
+ scale: float | None = None,
37
+ ) -> torch.Tensor:
38
+ """
39
+ Compute scaled dot-product attention.
40
+ It is part of the public SDPA execution path.
41
+
42
+ Args:
43
+ q: Query tensor in attention layout.
44
+ k: Key tensor in attention layout.
45
+ v: Value tensor in attention layout.
46
+ causal: Whether causal masking is enabled.
47
+ scale: Attention scaling factor.
48
+
49
+ Returns:
50
+ torch.Tensor: Function result value.
51
+ """
52
+ try:
53
+ from .sdpa import sdpa as _sdpa
54
+ except ImportError: # pragma: no cover - flattened kernel package layout
55
+ from sdpa import sdpa as _sdpa # type: ignore
56
+
57
+ return _sdpa(q, k, v, causal=causal, scale=scale)
58
+
59
+
60
+ __all__ = [
61
+ "__version__",
62
+ "sdpa",
63
+ "TiledAttentionError",
64
+ "UnsupportedPlatformError",
65
+ "InvalidShapeError",
66
+ "DTypeNotSupportedError",
67
+ "DependencyError",
68
+ ]
build/torch-cuda/_errors.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Custom exception types for tiledattention."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class TiledAttentionError(Exception):
7
+ """Base exception for tiledattention."""
8
+
9
+
10
+ class UnsupportedPlatformError(TiledAttentionError):
11
+ """Raised when runtime platform requirements are not satisfied."""
12
+
13
+
14
+ class InvalidShapeError(TiledAttentionError):
15
+ """Raised when q/k/v tensors do not match expected SDPA input contracts."""
16
+
17
+
18
+ class DTypeNotSupportedError(TiledAttentionError):
19
+ """Raised when input tensor dtypes are unsupported."""
20
+
21
+
22
+ class DependencyError(TiledAttentionError):
23
+ """Raised when a required dependency is missing."""
build/torch-cuda/_ops.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def get_backend() -> str:
4
+ """Detect the backend by inspecting torch."""
5
+ import torch
6
+
7
+ if hasattr(torch, "neuron"):
8
+ # Needs to be sorted before specific Torch builds, since Neuron
9
+ # extension can be loaded into e.g. CUDA Torch builds.
10
+ return "neuron"
11
+ elif torch.version.cuda is not None:
12
+ return "cuda"
13
+ elif torch.version.hip is not None:
14
+ return "rocm"
15
+ elif torch.backends.mps.is_available():
16
+ return "metal"
17
+ elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
18
+ return "xpu"
19
+ else:
20
+ return "cpu"
21
+
22
+
23
+ def _find_ops_name() -> str:
24
+ kernel_name = "tiledattention"
25
+ unique_id = "703d09c_dirty"
26
+ backend = get_backend()
27
+ return f"_{kernel_name}_{backend}_{unique_id}"
28
+
29
+
30
+ _OPS_NAME = _find_ops_name()
31
+
32
+ ops = getattr(torch.ops, _OPS_NAME)
33
+
34
+ def add_op_namespace_prefix(op_name: str) -> str:
35
+ """
36
+ Prefix op by namespace.
37
+ """
38
+ return f"{_OPS_NAME}::{op_name}"
build/torch-cuda/_runtime.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Runtime checks and environment introspection for tiledattention."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import os
7
+ from threading import Lock
8
+ from types import ModuleType
9
+
10
+ try:
11
+ from ._errors import DependencyError, UnsupportedPlatformError
12
+ except ImportError: # pragma: no cover - flattened kernel package layout
13
+ from _errors import DependencyError, UnsupportedPlatformError # type: ignore
14
+
15
+ _SUPPORTED_CC_MAJORS = {10, 12}
16
+ _runtime_ready = False
17
+ _runtime_lock = Lock()
18
+ _cached_torch: ModuleType | None = None
19
+ _cached_cupy: ModuleType | None = None
20
+ _cached_cutile: ModuleType | None = None
21
+
22
+
23
+ def _get_torch_module() -> ModuleType:
24
+ """
25
+ Get torch module.
26
+ It supports runtime dependency loading and platform validation.
27
+
28
+ Returns:
29
+ ModuleType: Function result value.
30
+ """
31
+ global _cached_torch
32
+ if _cached_torch is not None:
33
+ return _cached_torch
34
+
35
+ try:
36
+ _cached_torch = importlib.import_module("torch")
37
+ return _cached_torch
38
+ except ModuleNotFoundError as exc:
39
+ raise DependencyError(
40
+ "PyTorch is required for tiledattention. Install a CUDA-enabled torch build first."
41
+ ) from exc
42
+
43
+
44
+ def get_torch_module() -> ModuleType:
45
+ """
46
+ Get torch module.
47
+ It supports runtime dependency loading and platform validation.
48
+
49
+ Returns:
50
+ ModuleType: Function result value.
51
+ """
52
+ return _get_torch_module()
53
+
54
+
55
+ def _get_cupy_module() -> ModuleType:
56
+ """
57
+ Get cupy module.
58
+ It supports runtime dependency loading and platform validation.
59
+
60
+ Returns:
61
+ ModuleType: Function result value.
62
+ """
63
+ global _cached_cupy
64
+ if _cached_cupy is not None:
65
+ return _cached_cupy
66
+
67
+ try:
68
+ _cached_cupy = importlib.import_module("cupy")
69
+ return _cached_cupy
70
+ except ModuleNotFoundError as exc:
71
+ raise DependencyError(
72
+ "CuPy is required at runtime. For CUDA 13 environments, install a compatible wheel (e.g., pip install cupy-cuda13x)."
73
+ ) from exc
74
+
75
+
76
+ def get_cupy_module() -> ModuleType:
77
+ """
78
+ Get cupy module.
79
+ It supports runtime dependency loading and platform validation.
80
+
81
+ Returns:
82
+ ModuleType: Function result value.
83
+ """
84
+ return _get_cupy_module()
85
+
86
+
87
+ def _cutile_candidates() -> tuple[str, ...]:
88
+ """
89
+ Internal helper for cutile candidates.
90
+ It supports runtime dependency loading and platform validation.
91
+
92
+ Returns:
93
+ tuple[str, ...]: Function result value.
94
+ """
95
+ raw = os.getenv("TILEDATTENTION_CUTILE_MODULE", "").strip()
96
+ if raw:
97
+ parsed = tuple(part.strip() for part in raw.split(",") if part.strip())
98
+ if parsed:
99
+ return parsed
100
+ return ("cutile", "cuda.tile", "cuda_tile")
101
+
102
+
103
+ def _get_cutile_module() -> ModuleType:
104
+ """
105
+ Get cutile module.
106
+ It supports runtime dependency loading and platform validation.
107
+
108
+ Returns:
109
+ ModuleType: Function result value.
110
+ """
111
+ global _cached_cutile
112
+ if _cached_cutile is not None:
113
+ return _cached_cutile
114
+
115
+ candidates = _cutile_candidates()
116
+ for module_name in candidates:
117
+ try:
118
+ _cached_cutile = importlib.import_module(module_name)
119
+ return _cached_cutile
120
+ except ModuleNotFoundError:
121
+ continue
122
+
123
+ candidate_text = ", ".join(candidates)
124
+ raise DependencyError(
125
+ "cuTile Python module could not be imported. "
126
+ f"Tried: {candidate_text}. "
127
+ "Install NVIDIA CUDA Tile Python (e.g., pip install cuda-tile) or set "
128
+ "TILEDATTENTION_CUTILE_MODULE=<module_name>."
129
+ )
130
+
131
+
132
+ def get_cutile_module() -> ModuleType:
133
+ """
134
+ Get cutile module.
135
+ It supports runtime dependency loading and platform validation.
136
+
137
+ Returns:
138
+ ModuleType: Function result value.
139
+ """
140
+ return _get_cutile_module()
141
+
142
+
143
+ def _query_compute_capability(torch_mod: ModuleType) -> tuple[int, int]:
144
+ """
145
+ Internal helper for query compute capability.
146
+ It supports runtime dependency loading and platform validation.
147
+
148
+ Args:
149
+ torch_mod: Imported torch module instance.
150
+
151
+ Returns:
152
+ tuple[int, int]: Function result value.
153
+ """
154
+ try:
155
+ device_index = torch_mod.cuda.current_device()
156
+ major, minor = torch_mod.cuda.get_device_capability(device_index)
157
+ return int(major), int(minor)
158
+ except Exception as exc: # pragma: no cover - defensive path
159
+ raise UnsupportedPlatformError(
160
+ "Unable to query GPU compute capability from torch.cuda."
161
+ ) from exc
162
+
163
+
164
+ def require_supported_runtime() -> None:
165
+ """
166
+ Run require supported runtime.
167
+ It supports runtime dependency loading and platform validation.
168
+ """
169
+
170
+ global _runtime_ready
171
+
172
+ with _runtime_lock:
173
+ if _runtime_ready:
174
+ return
175
+
176
+ torch_mod = _get_torch_module()
177
+
178
+ if not bool(torch_mod.cuda.is_available()):
179
+ raise UnsupportedPlatformError(
180
+ "tiledattention requires CUDA + Blackwell GPU. "
181
+ "torch.cuda.is_available() is False."
182
+ )
183
+
184
+ major, minor = _query_compute_capability(torch_mod)
185
+ if major not in _SUPPORTED_CC_MAJORS:
186
+ raise UnsupportedPlatformError(
187
+ f"Unsupported GPU: compute capability {major}.{minor}; "
188
+ "requires Blackwell-class GPU (10.x or 12.x)."
189
+ )
190
+
191
+ _get_cupy_module()
192
+ _get_cutile_module()
193
+
194
+ _runtime_ready = True
195
+
196
+
197
+ def _reset_runtime_cache_for_tests() -> None:
198
+ """
199
+ Internal helper for reset runtime cache for tests.
200
+ It supports runtime dependency loading and platform validation.
201
+ """
202
+
203
+ global _runtime_ready, _cached_torch, _cached_cupy, _cached_cutile
204
+ with _runtime_lock:
205
+ _runtime_ready = False
206
+ _cached_torch = None
207
+ _cached_cupy = None
208
+ _cached_cutile = None
build/torch-cuda/_version.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Version helpers for tiledattention."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib.metadata import PackageNotFoundError, version
6
+
7
+
8
+ def _resolve_version() -> str:
9
+ """
10
+ Resolve version.
11
+ It is used by the tiledattention runtime and tooling.
12
+
13
+ Returns:
14
+ str: Function result value.
15
+ """
16
+ try:
17
+ return version("tiledattention")
18
+ except PackageNotFoundError:
19
+ return "0.0.0"
20
+
21
+
22
+ __version__ = _resolve_version()
build/torch-cuda/kernels/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """Kernel entrypoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .flash_fwd import run_flash_fwd
6
+
7
+ __all__ = ["run_flash_fwd"]
build/torch-cuda/kernels/compile_cache.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """In-memory kernel cache for cuTile kernel factories."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+
7
+ KernelCallable = Callable[..., object]
8
+ KernelKey = tuple[object, ...]
9
+
10
+ _KERNEL_CACHE: dict[KernelKey, KernelCallable] = {}
11
+
12
+
13
+ def get_kernel(key: KernelKey, factory: Callable[[], KernelCallable]) -> KernelCallable:
14
+ """
15
+ Get kernel.
16
+ It manages in-memory reuse of compiled kernel callables.
17
+
18
+ Args:
19
+ key: Cache key for kernel lookup.
20
+ factory: Factory that creates a kernel callable.
21
+
22
+ Returns:
23
+ KernelCallable: Function result value.
24
+ """
25
+ kernel = _KERNEL_CACHE.get(key)
26
+ if kernel is None:
27
+ kernel = factory()
28
+ _KERNEL_CACHE[key] = kernel
29
+ return kernel
30
+
31
+
32
+ def reset_cache_for_tests() -> None:
33
+ """
34
+ Run reset cache for tests.
35
+ It manages in-memory reuse of compiled kernel callables.
36
+ """
37
+ _KERNEL_CACHE.clear()
build/torch-cuda/kernels/flash_fwd.py ADDED
@@ -0,0 +1,978 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashAttention-style forward kernel launcher."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any
7
+
8
+ try:
9
+ from .. import _runtime
10
+ from .._errors import DTypeNotSupportedError
11
+ except ImportError: # pragma: no cover - flattened kernel package layout
12
+ import _runtime # type: ignore
13
+ from _errors import DTypeNotSupportedError # type: ignore
14
+
15
+ from .compile_cache import get_kernel
16
+
17
+ _DEFAULT_TM = 64
18
+ _DEFAULT_TN = 64
19
+ _NEG_LARGE = -1.0e30
20
+ _DIRECT_HEAD_DIMS = frozenset({64, 128})
21
+ _ALIGNED_FASTPATH_HEAD_DIMS = frozenset({64, 128})
22
+ _ALIGNED_FASTPATH_MIN_SEQ_BY_HEAD_DIM = {
23
+ 64: 1024,
24
+ 128: 2048,
25
+ }
26
+ _CHUNKED_HEAD_DIM_PARTS = {
27
+ 96: (64, 32),
28
+ 160: (128, 32),
29
+ }
30
+
31
+
32
+ def _ct_dtype_for_torch_dtype(ct: Any, torch_dtype: Any) -> Any:
33
+ """
34
+ Internal helper for ct dtype for torch dtype.
35
+ It is used during kernel configuration, compilation, or launch.
36
+
37
+ Args:
38
+ ct: Imported cuTile module instance.
39
+ torch_dtype: Torch dtype value to map into cuTile dtype.
40
+
41
+ Returns:
42
+ Any: Function result value.
43
+ """
44
+ torch_mod = _runtime.get_torch_module()
45
+ if torch_dtype == torch_mod.float16:
46
+ return ct.float16
47
+ if torch_dtype == torch_mod.bfloat16:
48
+ return ct.bfloat16
49
+ raise DTypeNotSupportedError("q dtype must be torch.float16 or torch.bfloat16.")
50
+
51
+
52
+ def _get_env_int(name: str, default: int | None) -> int | None:
53
+ """
54
+ Get env int.
55
+ It is used during kernel configuration, compilation, or launch.
56
+
57
+ Args:
58
+ name: Identifier or metric name.
59
+ default: Default value used when environment is unset.
60
+
61
+ Returns:
62
+ int | None: Function result value.
63
+ """
64
+ raw = os.getenv(name)
65
+ if raw is None or raw.strip() == "":
66
+ return default
67
+ try:
68
+ return int(raw)
69
+ except ValueError as exc: # pragma: no cover - defensive
70
+ raise ValueError(f"Environment variable {name} must be an integer.") from exc
71
+
72
+
73
+ def _resolve_tile_config() -> tuple[int, int]:
74
+ """
75
+ Resolve tile config.
76
+ It is used during kernel configuration, compilation, or launch.
77
+
78
+ Returns:
79
+ tuple[int, int]: Function result value.
80
+ """
81
+ tile_m = _get_env_int("TILEDATTN_TILE_M", None)
82
+ tile_n = _get_env_int("TILEDATTN_TILE_N", None)
83
+ if tile_m is None:
84
+ tile_m = _DEFAULT_TM
85
+ if tile_n is None:
86
+ tile_n = _DEFAULT_TN
87
+ if tile_m <= 0 or tile_n <= 0:
88
+ raise ValueError("TILEDATTN_TILE_M and TILEDATTN_TILE_N must be positive.")
89
+ return tile_m, tile_n
90
+
91
+
92
+ def _resolve_kernel_options() -> tuple[int, int | None, int | None]:
93
+ """
94
+ Resolve kernel options.
95
+ It is used during kernel configuration, compilation, or launch.
96
+
97
+ Returns:
98
+ tuple[int, int | None, int | None]: Function result value.
99
+ """
100
+ opt_level = _get_env_int("TILEDATTN_KERNEL_OPT_LEVEL", 3)
101
+ occupancy = _get_env_int("TILEDATTN_KERNEL_OCCUPANCY", None)
102
+ num_ctas = _get_env_int("TILEDATTN_KERNEL_NUM_CTAS", None)
103
+ assert opt_level is not None
104
+ return opt_level, occupancy, num_ctas
105
+
106
+
107
+ def _default_accum_mode_for_shape(*, seq_len: int, head_dim: int, causal: bool) -> str:
108
+ # Empirical policy from reduced benchmark:
109
+ # - D=64 long-ish non-causal shapes prefer fp16 accumulation.
110
+ # - D=128 long non-causal shapes prefer fp32 accumulation.
111
+ """
112
+ Internal helper for default accum mode for shape.
113
+ It is used during kernel configuration, compilation, or launch.
114
+
115
+ Args:
116
+ seq_len: Sequence length used for heuristic decisions.
117
+ head_dim: Attention head dimension.
118
+ causal: Whether causal masking is enabled.
119
+
120
+ Returns:
121
+ str: Function result value.
122
+ """
123
+ if (not causal) and head_dim == 64 and seq_len >= 1024:
124
+ return "fp16"
125
+ return "fp32"
126
+
127
+
128
+ def _resolve_accum_mode(*, seq_len: int, head_dim: int, causal: bool) -> str:
129
+ """
130
+ Resolve accum mode.
131
+ It is used during kernel configuration, compilation, or launch.
132
+
133
+ Args:
134
+ seq_len: Sequence length used for heuristic decisions.
135
+ head_dim: Attention head dimension.
136
+ causal: Whether causal masking is enabled.
137
+
138
+ Returns:
139
+ str: Function result value.
140
+ """
141
+ raw_mode = os.getenv("TILEDATTN_ACCUM_MODE")
142
+ if raw_mode is None or raw_mode.strip() == "":
143
+ return _default_accum_mode_for_shape(seq_len=seq_len, head_dim=head_dim, causal=causal)
144
+ mode = raw_mode.strip().lower()
145
+ if mode == "auto":
146
+ return _default_accum_mode_for_shape(seq_len=seq_len, head_dim=head_dim, causal=causal)
147
+ if mode not in {"fp32", "fp16"}:
148
+ raise ValueError(
149
+ f"Unsupported TILEDATTN_ACCUM_MODE={mode!r}. Use one of: auto, fp32, fp16."
150
+ )
151
+ return mode
152
+
153
+
154
+ def _resolve_kernel_head_dim(head_dim: int) -> tuple[int, int]:
155
+ """
156
+ Resolve kernel head dim.
157
+ It is used during kernel configuration, compilation, or launch.
158
+
159
+ Args:
160
+ head_dim: Attention head dimension.
161
+
162
+ Returns:
163
+ tuple[int, int]: Function result value.
164
+ """
165
+ if head_dim in _DIRECT_HEAD_DIMS:
166
+ return head_dim, 0
167
+ kernel_head_dim = 1 << (head_dim - 1).bit_length()
168
+ return kernel_head_dim, kernel_head_dim - head_dim
169
+
170
+
171
+ def _resolve_chunk_plan(head_dim: int) -> tuple[tuple[int, int], ...] | None:
172
+ """
173
+ Resolve chunk plan.
174
+ It is used during kernel configuration, compilation, or launch.
175
+
176
+ Args:
177
+ head_dim: Attention head dimension.
178
+
179
+ Returns:
180
+ tuple[tuple[int, int], ...] | None: Function result value.
181
+ """
182
+ enabled = os.getenv("TILEDATTN_CHUNKED_HEAD_DIMS", "").strip()
183
+ if enabled == "":
184
+ return None
185
+ try:
186
+ enabled_dims = {int(x.strip()) for x in enabled.split(",") if x.strip() != ""}
187
+ except ValueError as exc:
188
+ raise ValueError(
189
+ "Environment variable TILEDATTN_CHUNKED_HEAD_DIMS must be a comma-separated list of integers."
190
+ ) from exc
191
+ if head_dim not in enabled_dims:
192
+ return None
193
+ parts = _CHUNKED_HEAD_DIM_PARTS.get(head_dim)
194
+ if parts is None:
195
+ return None
196
+ offset = 0
197
+ plan: list[tuple[int, int]] = []
198
+ for width in parts:
199
+ plan.append((offset, width))
200
+ offset += width
201
+ return tuple(plan)
202
+
203
+
204
+ def _should_use_aligned_noncausal_fastpath(
205
+ *,
206
+ seq_len: int,
207
+ head_dim: int,
208
+ causal: bool,
209
+ pad_dim: int,
210
+ chunk_plan: tuple[tuple[int, int], ...] | None,
211
+ tile_m: int,
212
+ tile_n: int,
213
+ ) -> bool:
214
+ """
215
+ Internal helper for should use aligned noncausal fastpath.
216
+ It is used during kernel configuration, compilation, or launch.
217
+
218
+ Args:
219
+ seq_len: Sequence length used for heuristic decisions.
220
+ head_dim: Attention head dimension.
221
+ causal: Whether causal masking is enabled.
222
+ pad_dim: Function argument.
223
+ chunk_plan: Head-dimension chunk decomposition plan.
224
+ tile_m: Tile size in the query-row dimension.
225
+ tile_n: Tile size in the key/value-column dimension.
226
+
227
+ Returns:
228
+ bool: Function result value.
229
+ """
230
+ if os.getenv("TILEDATTN_DISABLE_ALIGNED_FASTPATH", "").strip() not in {"", "0", "false", "False"}:
231
+ return False
232
+ if causal:
233
+ return False
234
+ if chunk_plan is not None:
235
+ return False
236
+ if pad_dim != 0:
237
+ return False
238
+ if head_dim not in _ALIGNED_FASTPATH_HEAD_DIMS:
239
+ return False
240
+ min_seq = _ALIGNED_FASTPATH_MIN_SEQ_BY_HEAD_DIM.get(head_dim, 2048)
241
+ if seq_len < min_seq:
242
+ return False
243
+ return (seq_len % tile_m == 0) and (seq_len % tile_n == 0)
244
+
245
+
246
+ def _default_tile_config_for_shape(*, seq_len: int, head_dim: int, causal: bool) -> tuple[int, int]:
247
+ """
248
+ Internal helper for default tile config for shape.
249
+ It is used during kernel configuration, compilation, or launch.
250
+
251
+ Args:
252
+ seq_len: Sequence length used for heuristic decisions.
253
+ head_dim: Attention head dimension.
254
+ causal: Whether causal masking is enabled.
255
+
256
+ Returns:
257
+ tuple[int, int]: Function result value.
258
+ """
259
+ tile_m = _DEFAULT_TM
260
+ tile_n = _DEFAULT_TN
261
+ # Mid/long non-causal D=128 favors wider N tiles in current tuning results.
262
+ if (not causal) and head_dim == 128 and seq_len >= 2048:
263
+ tile_n = 128
264
+ return tile_m, tile_n
265
+
266
+
267
+ def make_flashattn_fwd_kernel(
268
+ tile_m: int,
269
+ tile_n: int,
270
+ head_dim: int,
271
+ *,
272
+ dtype: Any,
273
+ causal: bool,
274
+ accum_mode: str,
275
+ opt_level: int,
276
+ occupancy: int | None,
277
+ num_ctas: int | None,
278
+ ):
279
+ """
280
+ Create flashattn fwd kernel.
281
+ It is used during kernel configuration, compilation, or launch.
282
+
283
+ Args:
284
+ tile_m: Tile size in the query-row dimension.
285
+ tile_n: Tile size in the key/value-column dimension.
286
+ head_dim: Attention head dimension.
287
+ dtype: Target data type used for computation.
288
+ causal: Whether causal masking is enabled.
289
+ accum_mode: Function argument.
290
+ opt_level: Kernel optimization level forwarded to cuTile.
291
+ occupancy: Optional occupancy hint for kernel launch.
292
+ num_ctas: Optional CTA count hint for kernel launch.
293
+
294
+ Returns:
295
+ object: Function result value.
296
+ """
297
+ ct = _runtime.get_cutile_module()
298
+ kernel_kwargs: dict[str, Any] = {"opt_level": opt_level}
299
+ if occupancy is not None:
300
+ kernel_kwargs["occupancy"] = occupancy
301
+ if num_ctas is not None:
302
+ kernel_kwargs["num_ctas"] = num_ctas
303
+
304
+ if accum_mode == "fp16":
305
+
306
+ @ct.kernel(**kernel_kwargs)
307
+ def flash_fwd_kernel_fp16acc(q, k_t, v, out, scale):
308
+ """
309
+ Run flash fwd kernel fp16acc.
310
+ It is used during kernel configuration, compilation, or launch.
311
+
312
+ Args:
313
+ q: Query tensor in attention layout.
314
+ k_t: Transposed key tensor view used by the kernel.
315
+ v: Value tensor in attention layout.
316
+ out: Output tensor buffer.
317
+ scale: Attention scaling factor.
318
+ """
319
+ bh_idx = ct.bid(0)
320
+ q_tile_idx = ct.bid(1)
321
+
322
+ # Keep GEMM inputs in low precision to allow tensor-core codegen.
323
+ q_tile = ct.load(q, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, head_dim))
324
+
325
+ seq_len = q.shape[1]
326
+ num_k_tiles = ct.cdiv(seq_len, tile_n)
327
+ if causal:
328
+ # For causal mode, this query tile never attends beyond its last row index.
329
+ causal_cols = ct.minimum((q_tile_idx + 1) * tile_m, seq_len)
330
+ num_k_tiles = ct.cdiv(causal_cols, tile_n)
331
+
332
+ row = q_tile_idx * tile_m + ct.arange(tile_m, dtype=ct.int32)
333
+ row = ct.expand_dims(row, 1)
334
+ row = ct.expand_dims(row, 0)
335
+ row_in_bounds = row < seq_len
336
+
337
+ m_i = ct.full((1, tile_m, 1), _NEG_LARGE, ct.float32)
338
+ l_i = ct.zeros((1, tile_m, 1), ct.float32)
339
+ acc = ct.zeros((1, tile_m, head_dim), dtype)
340
+
341
+ for k_tile_idx in range(num_k_tiles):
342
+ k_tile_t = ct.load(k_t, index=(bh_idx, 0, k_tile_idx), shape=(1, head_dim, tile_n))
343
+
344
+ score = ct.matmul(q_tile, k_tile_t)
345
+ score = ct.astype(score, ct.float32) * scale
346
+
347
+ col = k_tile_idx * tile_n + ct.arange(tile_n, dtype=ct.int32)
348
+ col = ct.expand_dims(col, 0)
349
+ col = ct.expand_dims(col, 0)
350
+ key_in_bounds = col < seq_len
351
+ valid = row_in_bounds & key_in_bounds
352
+ if causal:
353
+ valid = valid & (row >= col)
354
+
355
+ score = ct.where(valid, score, _NEG_LARGE)
356
+
357
+ tile_max = ct.max(score, axis=2, keepdims=True)
358
+ m_next = ct.maximum(m_i, tile_max)
359
+ alpha = ct.exp(m_i - m_next)
360
+
361
+ p = ct.exp(score - m_next)
362
+ l_i = l_i * alpha + ct.sum(p, axis=2, keepdims=True)
363
+
364
+ v_tile = ct.load(v, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, head_dim))
365
+ p_lowp = ct.astype(p, dtype)
366
+ alpha_lowp = ct.astype(alpha, dtype)
367
+ acc = acc * alpha_lowp + ct.matmul(p_lowp, v_tile)
368
+ m_i = m_next
369
+
370
+ safe_l = ct.where(row_in_bounds, l_i, 1.0)
371
+ # Row-wise reciprocal avoids a full per-element divide over head_dim.
372
+ inv_l = 1.0 / safe_l
373
+ out_tile = acc * ct.astype(inv_l, dtype)
374
+ out_tile = ct.where(row_in_bounds, out_tile, 0.0)
375
+ ct.store(out, index=(bh_idx, q_tile_idx, 0), tile=out_tile)
376
+
377
+ return flash_fwd_kernel_fp16acc
378
+
379
+ @ct.kernel(**kernel_kwargs)
380
+ def flash_fwd_kernel_fp32acc(q, k_t, v, out, scale):
381
+ """
382
+ Run flash fwd kernel fp32acc.
383
+ It is used during kernel configuration, compilation, or launch.
384
+
385
+ Args:
386
+ q: Query tensor in attention layout.
387
+ k_t: Transposed key tensor view used by the kernel.
388
+ v: Value tensor in attention layout.
389
+ out: Output tensor buffer.
390
+ scale: Attention scaling factor.
391
+ """
392
+ bh_idx = ct.bid(0)
393
+ q_tile_idx = ct.bid(1)
394
+
395
+ # Keep GEMM inputs in low precision to allow tensor-core codegen.
396
+ q_tile = ct.load(q, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, head_dim))
397
+
398
+ seq_len = q.shape[1]
399
+ num_k_tiles = ct.cdiv(seq_len, tile_n)
400
+ if causal:
401
+ # For causal mode, this query tile never attends beyond its last row index.
402
+ causal_cols = ct.minimum((q_tile_idx + 1) * tile_m, seq_len)
403
+ num_k_tiles = ct.cdiv(causal_cols, tile_n)
404
+
405
+ row = q_tile_idx * tile_m + ct.arange(tile_m, dtype=ct.int32)
406
+ row = ct.expand_dims(row, 1)
407
+ row = ct.expand_dims(row, 0)
408
+ row_in_bounds = row < seq_len
409
+
410
+ m_i = ct.full((1, tile_m, 1), _NEG_LARGE, ct.float32)
411
+ l_i = ct.zeros((1, tile_m, 1), ct.float32)
412
+ acc = ct.zeros((1, tile_m, head_dim), ct.float32)
413
+
414
+ for k_tile_idx in range(num_k_tiles):
415
+ k_tile_t = ct.load(k_t, index=(bh_idx, 0, k_tile_idx), shape=(1, head_dim, tile_n))
416
+
417
+ score = ct.matmul(q_tile, k_tile_t)
418
+ score = ct.astype(score, ct.float32) * scale
419
+
420
+ col = k_tile_idx * tile_n + ct.arange(tile_n, dtype=ct.int32)
421
+ col = ct.expand_dims(col, 0)
422
+ col = ct.expand_dims(col, 0)
423
+ key_in_bounds = col < seq_len
424
+ valid = row_in_bounds & key_in_bounds
425
+ if causal:
426
+ valid = valid & (row >= col)
427
+
428
+ score = ct.where(valid, score, _NEG_LARGE)
429
+
430
+ tile_max = ct.max(score, axis=2, keepdims=True)
431
+ m_next = ct.maximum(m_i, tile_max)
432
+ alpha = ct.exp(m_i - m_next)
433
+
434
+ p = ct.exp(score - m_next)
435
+ l_i = l_i * alpha + ct.sum(p, axis=2, keepdims=True)
436
+
437
+ v_tile = ct.load(v, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, head_dim))
438
+ p_lowp = ct.astype(p, dtype)
439
+ acc_update = ct.matmul(p_lowp, v_tile)
440
+ acc = acc * alpha + ct.astype(acc_update, ct.float32)
441
+ m_i = m_next
442
+
443
+ safe_l = ct.where(row_in_bounds, l_i, 1.0)
444
+ # Row-wise reciprocal avoids a full per-element divide over head_dim.
445
+ inv_l = 1.0 / safe_l
446
+ out_tile = acc * inv_l
447
+ out_tile = ct.where(row_in_bounds, out_tile, 0.0)
448
+ out_tile = ct.astype(out_tile, dtype)
449
+ ct.store(out, index=(bh_idx, q_tile_idx, 0), tile=out_tile)
450
+
451
+ return flash_fwd_kernel_fp32acc
452
+
453
+
454
+ def make_flashattn_fwd_kernel_aligned_noncausal(
455
+ tile_m: int,
456
+ tile_n: int,
457
+ head_dim: int,
458
+ *,
459
+ dtype: Any,
460
+ accum_mode: str,
461
+ opt_level: int,
462
+ occupancy: int | None,
463
+ num_ctas: int | None,
464
+ ):
465
+ """
466
+ Create flashattn fwd kernel aligned noncausal.
467
+ It is used during kernel configuration, compilation, or launch.
468
+
469
+ Args:
470
+ tile_m: Tile size in the query-row dimension.
471
+ tile_n: Tile size in the key/value-column dimension.
472
+ head_dim: Attention head dimension.
473
+ dtype: Target data type used for computation.
474
+ accum_mode: Function argument.
475
+ opt_level: Kernel optimization level forwarded to cuTile.
476
+ occupancy: Optional occupancy hint for kernel launch.
477
+ num_ctas: Optional CTA count hint for kernel launch.
478
+
479
+ Returns:
480
+ object: Function result value.
481
+ """
482
+ ct = _runtime.get_cutile_module()
483
+ kernel_kwargs: dict[str, Any] = {"opt_level": opt_level}
484
+ if occupancy is not None:
485
+ kernel_kwargs["occupancy"] = occupancy
486
+ if num_ctas is not None:
487
+ kernel_kwargs["num_ctas"] = num_ctas
488
+
489
+ if accum_mode == "fp16":
490
+
491
+ @ct.kernel(**kernel_kwargs)
492
+ def flash_fwd_kernel_fp16acc_aligned(q, k_t, v, out, scale):
493
+ """
494
+ Run flash fwd kernel fp16acc aligned.
495
+ It is used during kernel configuration, compilation, or launch.
496
+
497
+ Args:
498
+ q: Query tensor in attention layout.
499
+ k_t: Transposed key tensor view used by the kernel.
500
+ v: Value tensor in attention layout.
501
+ out: Output tensor buffer.
502
+ scale: Attention scaling factor.
503
+ """
504
+ bh_idx = ct.bid(0)
505
+ q_tile_idx = ct.bid(1)
506
+
507
+ q_tile = ct.load(q, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, head_dim))
508
+ seq_len = q.shape[1]
509
+ num_k_tiles = seq_len // tile_n
510
+
511
+ m_i = ct.full((1, tile_m, 1), _NEG_LARGE, ct.float32)
512
+ l_i = ct.zeros((1, tile_m, 1), ct.float32)
513
+ acc = ct.zeros((1, tile_m, head_dim), dtype)
514
+
515
+ for k_tile_idx in range(num_k_tiles):
516
+ k_tile_t = ct.load(k_t, index=(bh_idx, 0, k_tile_idx), shape=(1, head_dim, tile_n))
517
+ score = ct.astype(ct.matmul(q_tile, k_tile_t), ct.float32) * scale
518
+
519
+ tile_max = ct.max(score, axis=2, keepdims=True)
520
+ m_next = ct.maximum(m_i, tile_max)
521
+ alpha = ct.exp(m_i - m_next)
522
+
523
+ p = ct.exp(score - m_next)
524
+ l_i = l_i * alpha + ct.sum(p, axis=2, keepdims=True)
525
+
526
+ v_tile = ct.load(v, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, head_dim))
527
+ p_lowp = ct.astype(p, dtype)
528
+ alpha_lowp = ct.astype(alpha, dtype)
529
+ acc = acc * alpha_lowp + ct.matmul(p_lowp, v_tile)
530
+ m_i = m_next
531
+
532
+ inv_l = 1.0 / l_i
533
+ out_tile = acc * ct.astype(inv_l, dtype)
534
+ ct.store(out, index=(bh_idx, q_tile_idx, 0), tile=out_tile)
535
+
536
+ return flash_fwd_kernel_fp16acc_aligned
537
+
538
+ @ct.kernel(**kernel_kwargs)
539
+ def flash_fwd_kernel_fp32acc_aligned(q, k_t, v, out, scale):
540
+ """
541
+ Run flash fwd kernel fp32acc aligned.
542
+ It is used during kernel configuration, compilation, or launch.
543
+
544
+ Args:
545
+ q: Query tensor in attention layout.
546
+ k_t: Transposed key tensor view used by the kernel.
547
+ v: Value tensor in attention layout.
548
+ out: Output tensor buffer.
549
+ scale: Attention scaling factor.
550
+ """
551
+ bh_idx = ct.bid(0)
552
+ q_tile_idx = ct.bid(1)
553
+
554
+ q_tile = ct.load(q, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, head_dim))
555
+ seq_len = q.shape[1]
556
+ num_k_tiles = seq_len // tile_n
557
+
558
+ m_i = ct.full((1, tile_m, 1), _NEG_LARGE, ct.float32)
559
+ l_i = ct.zeros((1, tile_m, 1), ct.float32)
560
+ acc = ct.zeros((1, tile_m, head_dim), ct.float32)
561
+
562
+ for k_tile_idx in range(num_k_tiles):
563
+ k_tile_t = ct.load(k_t, index=(bh_idx, 0, k_tile_idx), shape=(1, head_dim, tile_n))
564
+ score = ct.astype(ct.matmul(q_tile, k_tile_t), ct.float32) * scale
565
+
566
+ tile_max = ct.max(score, axis=2, keepdims=True)
567
+ m_next = ct.maximum(m_i, tile_max)
568
+ alpha = ct.exp(m_i - m_next)
569
+
570
+ p = ct.exp(score - m_next)
571
+ l_i = l_i * alpha + ct.sum(p, axis=2, keepdims=True)
572
+
573
+ v_tile = ct.load(v, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, head_dim))
574
+ p_lowp = ct.astype(p, dtype)
575
+ acc = acc * alpha + ct.astype(ct.matmul(p_lowp, v_tile), ct.float32)
576
+ m_i = m_next
577
+
578
+ out_tile = ct.astype(acc * (1.0 / l_i), dtype)
579
+ ct.store(out, index=(bh_idx, q_tile_idx, 0), tile=out_tile)
580
+
581
+ return flash_fwd_kernel_fp32acc_aligned
582
+
583
+
584
+ def make_flashattn_fwd_kernel_chunked(
585
+ tile_m: int,
586
+ tile_n: int,
587
+ head_dim: int,
588
+ *,
589
+ chunk_plan: tuple[tuple[int, int], ...],
590
+ dtype: Any,
591
+ causal: bool,
592
+ accum_mode: str,
593
+ opt_level: int,
594
+ occupancy: int | None,
595
+ num_ctas: int | None,
596
+ ):
597
+ """
598
+ Create flashattn fwd kernel chunked.
599
+ It is used during kernel configuration, compilation, or launch.
600
+
601
+ Args:
602
+ tile_m: Tile size in the query-row dimension.
603
+ tile_n: Tile size in the key/value-column dimension.
604
+ head_dim: Attention head dimension.
605
+ chunk_plan: Head-dimension chunk decomposition plan.
606
+ dtype: Target data type used for computation.
607
+ causal: Whether causal masking is enabled.
608
+ accum_mode: Function argument.
609
+ opt_level: Kernel optimization level forwarded to cuTile.
610
+ occupancy: Optional occupancy hint for kernel launch.
611
+ num_ctas: Optional CTA count hint for kernel launch.
612
+
613
+ Returns:
614
+ object: Function result value.
615
+ """
616
+ ct = _runtime.get_cutile_module()
617
+ kernel_kwargs: dict[str, Any] = {"opt_level": opt_level}
618
+ if occupancy is not None:
619
+ kernel_kwargs["occupancy"] = occupancy
620
+ if num_ctas is not None:
621
+ kernel_kwargs["num_ctas"] = num_ctas
622
+ if len(chunk_plan) != 2:
623
+ raise ValueError(f"Chunked kernel currently expects exactly 2 chunks, got {chunk_plan!r}.")
624
+ (off0, w0), (off1, w1) = chunk_plan
625
+
626
+ if accum_mode == "fp16":
627
+
628
+ @ct.kernel(**kernel_kwargs)
629
+ def flash_fwd_kernel_fp16acc_chunked(q0, k0_t, v0, q1, k1_t, v1, out0, out1, scale):
630
+ """
631
+ Run flash fwd kernel fp16acc chunked.
632
+ It is used during kernel configuration, compilation, or launch.
633
+
634
+ Args:
635
+ q0: First query chunk for chunked head-dimension kernels.
636
+ k0_t: Transposed first key chunk.
637
+ v0: First value chunk for chunked execution.
638
+ q1: Second query chunk for chunked head-dimension kernels.
639
+ k1_t: Transposed second key chunk.
640
+ v1: Second value chunk for chunked execution.
641
+ out0: First output chunk buffer.
642
+ out1: Second output chunk buffer.
643
+ scale: Attention scaling factor.
644
+ """
645
+ bh_idx = ct.bid(0)
646
+ q_tile_idx = ct.bid(1)
647
+
648
+ q0_tile = ct.load(q0, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, w0))
649
+ q1_tile = ct.load(q1, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, w1))
650
+
651
+ seq_len = q0.shape[1]
652
+ num_k_tiles = ct.cdiv(seq_len, tile_n)
653
+ if causal:
654
+ causal_cols = ct.minimum((q_tile_idx + 1) * tile_m, seq_len)
655
+ num_k_tiles = ct.cdiv(causal_cols, tile_n)
656
+
657
+ row = q_tile_idx * tile_m + ct.arange(tile_m, dtype=ct.int32)
658
+ row = ct.expand_dims(row, 1)
659
+ row = ct.expand_dims(row, 0)
660
+ row_in_bounds = row < seq_len
661
+
662
+ m_i = ct.full((1, tile_m, 1), _NEG_LARGE, ct.float32)
663
+ l_i = ct.zeros((1, tile_m, 1), ct.float32)
664
+ acc0 = ct.zeros((1, tile_m, w0), dtype)
665
+ acc1 = ct.zeros((1, tile_m, w1), dtype)
666
+
667
+ for k_tile_idx in range(num_k_tiles):
668
+ k0_tile_t = ct.load(k0_t, index=(bh_idx, 0, k_tile_idx), shape=(1, w0, tile_n))
669
+ k1_tile_t = ct.load(k1_t, index=(bh_idx, 0, k_tile_idx), shape=(1, w1, tile_n))
670
+ score = ct.astype(ct.matmul(q0_tile, k0_tile_t), ct.float32) + ct.astype(
671
+ ct.matmul(q1_tile, k1_tile_t), ct.float32
672
+ )
673
+ score = score * scale
674
+
675
+ col = k_tile_idx * tile_n + ct.arange(tile_n, dtype=ct.int32)
676
+ col = ct.expand_dims(col, 0)
677
+ col = ct.expand_dims(col, 0)
678
+ key_in_bounds = col < seq_len
679
+ valid = row_in_bounds & key_in_bounds
680
+ if causal:
681
+ valid = valid & (row >= col)
682
+
683
+ score = ct.where(valid, score, _NEG_LARGE)
684
+
685
+ tile_max = ct.max(score, axis=2, keepdims=True)
686
+ m_next = ct.maximum(m_i, tile_max)
687
+ alpha = ct.exp(m_i - m_next)
688
+
689
+ p = ct.exp(score - m_next)
690
+ l_i = l_i * alpha + ct.sum(p, axis=2, keepdims=True)
691
+
692
+ p_lowp = ct.astype(p, dtype)
693
+ alpha_lowp = ct.astype(alpha, dtype)
694
+ v0_tile = ct.load(v0, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, w0))
695
+ v1_tile = ct.load(v1, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, w1))
696
+ acc0 = acc0 * alpha_lowp + ct.matmul(p_lowp, v0_tile)
697
+ acc1 = acc1 * alpha_lowp + ct.matmul(p_lowp, v1_tile)
698
+ m_i = m_next
699
+
700
+ safe_l = ct.where(row_in_bounds, l_i, 1.0)
701
+ inv_l = 1.0 / safe_l
702
+ inv_l_lowp = ct.astype(inv_l, dtype)
703
+ out0_tile = ct.where(row_in_bounds, acc0 * inv_l_lowp, 0.0)
704
+ out1_tile = ct.where(row_in_bounds, acc1 * inv_l_lowp, 0.0)
705
+ ct.store(out0, index=(bh_idx, q_tile_idx, 0), tile=out0_tile)
706
+ ct.store(out1, index=(bh_idx, q_tile_idx, 0), tile=out1_tile)
707
+
708
+ return flash_fwd_kernel_fp16acc_chunked
709
+
710
+ @ct.kernel(**kernel_kwargs)
711
+ def flash_fwd_kernel_fp32acc_chunked(q0, k0_t, v0, q1, k1_t, v1, out0, out1, scale):
712
+ """
713
+ Run flash fwd kernel fp32acc chunked.
714
+ It is used during kernel configuration, compilation, or launch.
715
+
716
+ Args:
717
+ q0: First query chunk for chunked head-dimension kernels.
718
+ k0_t: Transposed first key chunk.
719
+ v0: First value chunk for chunked execution.
720
+ q1: Second query chunk for chunked head-dimension kernels.
721
+ k1_t: Transposed second key chunk.
722
+ v1: Second value chunk for chunked execution.
723
+ out0: First output chunk buffer.
724
+ out1: Second output chunk buffer.
725
+ scale: Attention scaling factor.
726
+ """
727
+ bh_idx = ct.bid(0)
728
+ q_tile_idx = ct.bid(1)
729
+
730
+ q0_tile = ct.load(q0, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, w0))
731
+ q1_tile = ct.load(q1, index=(bh_idx, q_tile_idx, 0), shape=(1, tile_m, w1))
732
+
733
+ seq_len = q0.shape[1]
734
+ num_k_tiles = ct.cdiv(seq_len, tile_n)
735
+ if causal:
736
+ causal_cols = ct.minimum((q_tile_idx + 1) * tile_m, seq_len)
737
+ num_k_tiles = ct.cdiv(causal_cols, tile_n)
738
+
739
+ row = q_tile_idx * tile_m + ct.arange(tile_m, dtype=ct.int32)
740
+ row = ct.expand_dims(row, 1)
741
+ row = ct.expand_dims(row, 0)
742
+ row_in_bounds = row < seq_len
743
+
744
+ m_i = ct.full((1, tile_m, 1), _NEG_LARGE, ct.float32)
745
+ l_i = ct.zeros((1, tile_m, 1), ct.float32)
746
+ acc0 = ct.zeros((1, tile_m, w0), ct.float32)
747
+ acc1 = ct.zeros((1, tile_m, w1), ct.float32)
748
+
749
+ for k_tile_idx in range(num_k_tiles):
750
+ k0_tile_t = ct.load(k0_t, index=(bh_idx, 0, k_tile_idx), shape=(1, w0, tile_n))
751
+ k1_tile_t = ct.load(k1_t, index=(bh_idx, 0, k_tile_idx), shape=(1, w1, tile_n))
752
+ score = ct.astype(ct.matmul(q0_tile, k0_tile_t), ct.float32) + ct.astype(
753
+ ct.matmul(q1_tile, k1_tile_t), ct.float32
754
+ )
755
+ score = score * scale
756
+
757
+ col = k_tile_idx * tile_n + ct.arange(tile_n, dtype=ct.int32)
758
+ col = ct.expand_dims(col, 0)
759
+ col = ct.expand_dims(col, 0)
760
+ key_in_bounds = col < seq_len
761
+ valid = row_in_bounds & key_in_bounds
762
+ if causal:
763
+ valid = valid & (row >= col)
764
+
765
+ score = ct.where(valid, score, _NEG_LARGE)
766
+
767
+ tile_max = ct.max(score, axis=2, keepdims=True)
768
+ m_next = ct.maximum(m_i, tile_max)
769
+ alpha = ct.exp(m_i - m_next)
770
+
771
+ p = ct.exp(score - m_next)
772
+ l_i = l_i * alpha + ct.sum(p, axis=2, keepdims=True)
773
+
774
+ p_lowp = ct.astype(p, dtype)
775
+ v0_tile = ct.load(v0, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, w0))
776
+ v1_tile = ct.load(v1, index=(bh_idx, k_tile_idx, 0), shape=(1, tile_n, w1))
777
+ acc0 = acc0 * alpha + ct.astype(ct.matmul(p_lowp, v0_tile), ct.float32)
778
+ acc1 = acc1 * alpha + ct.astype(ct.matmul(p_lowp, v1_tile), ct.float32)
779
+ m_i = m_next
780
+
781
+ safe_l = ct.where(row_in_bounds, l_i, 1.0)
782
+ inv_l = 1.0 / safe_l
783
+ out0_tile = ct.astype(ct.where(row_in_bounds, acc0 * inv_l, 0.0), dtype)
784
+ out1_tile = ct.astype(ct.where(row_in_bounds, acc1 * inv_l, 0.0), dtype)
785
+ ct.store(out0, index=(bh_idx, q_tile_idx, 0), tile=out0_tile)
786
+ ct.store(out1, index=(bh_idx, q_tile_idx, 0), tile=out1_tile)
787
+
788
+ return flash_fwd_kernel_fp32acc_chunked
789
+
790
+
791
+ def _launch_cutile_kernel(
792
+ ct: Any,
793
+ cupy_mod: Any,
794
+ kernel: Any,
795
+ grid: tuple[int, int, int],
796
+ args: tuple[Any, ...],
797
+ ) -> None:
798
+ """
799
+ Internal helper for launch cutile kernel.
800
+ It is used during kernel configuration, compilation, or launch.
801
+
802
+ Args:
803
+ ct: Imported cuTile module instance.
804
+ cupy_mod: Imported CuPy module instance.
805
+ kernel: Compiled kernel object to launch.
806
+ grid: Function argument.
807
+ args: Parsed command-line arguments namespace.
808
+ """
809
+ torch_mod = _runtime.get_torch_module()
810
+ sync_mode = os.getenv("TILEDATTN_SYNC_MODE", "async").strip().lower()
811
+ stream = cupy_mod.cuda.get_current_stream()
812
+ if sync_mode == "strict":
813
+ torch_mod.cuda.synchronize()
814
+ ct.launch(stream, grid, kernel, args)
815
+ if sync_mode in {"strict", "post"}:
816
+ stream.synchronize()
817
+ elif sync_mode == "async":
818
+ pass
819
+ else:
820
+ raise ValueError(
821
+ f"Unsupported TILEDATTN_SYNC_MODE={sync_mode!r}. "
822
+ "Use one of: strict, post, async."
823
+ )
824
+
825
+
826
+ def run_flash_fwd(
827
+ q,
828
+ k,
829
+ v,
830
+ *,
831
+ causal: bool,
832
+ scale: float,
833
+ ):
834
+ """
835
+ Run flash fwd.
836
+ It is used during kernel configuration, compilation, or launch.
837
+
838
+ Args:
839
+ q: Query tensor in attention layout.
840
+ k: Key tensor in attention layout.
841
+ v: Value tensor in attention layout.
842
+ causal: Whether causal masking is enabled.
843
+ scale: Attention scaling factor.
844
+
845
+ Returns:
846
+ object: Function result value.
847
+ """
848
+ torch_mod = _runtime.get_torch_module()
849
+ cupy_mod = _runtime.get_cupy_module()
850
+ ct = _runtime.get_cutile_module()
851
+ opt_level, occupancy, num_ctas = _resolve_kernel_options()
852
+
853
+ batch, heads, seq_len, head_dim = map(int, q.shape)
854
+ accum_mode = _resolve_accum_mode(seq_len=seq_len, head_dim=head_dim, causal=causal)
855
+ bh = batch * heads
856
+ if os.getenv("TILEDATTN_TILE_M") or os.getenv("TILEDATTN_TILE_N"):
857
+ tile_m, tile_n = _resolve_tile_config()
858
+ else:
859
+ tile_m, tile_n = _default_tile_config_for_shape(
860
+ seq_len=seq_len, head_dim=head_dim, causal=causal
861
+ )
862
+
863
+ chunk_plan = _resolve_chunk_plan(head_dim)
864
+ if chunk_plan is not None:
865
+ kernel_head_dim = head_dim
866
+ pad_dim = 0
867
+ else:
868
+ kernel_head_dim, pad_dim = _resolve_kernel_head_dim(head_dim)
869
+
870
+ q_bh = q.contiguous().reshape(bh, seq_len, head_dim)
871
+ k_bh = k.contiguous().reshape(bh, seq_len, head_dim)
872
+ v_bh = v.contiguous().reshape(bh, seq_len, head_dim)
873
+ if pad_dim > 0:
874
+ pad = (0, pad_dim)
875
+ q_bh = torch_mod.nn.functional.pad(q_bh, pad)
876
+ k_bh = torch_mod.nn.functional.pad(k_bh, pad)
877
+ v_bh = torch_mod.nn.functional.pad(v_bh, pad)
878
+
879
+ # Keep K as a transpose view to avoid a per-call materialization copy.
880
+ k_t = k_bh.transpose(1, 2)
881
+
882
+ out_bh = torch_mod.empty_like(q_bh)
883
+
884
+ out_ct_dtype = _ct_dtype_for_torch_dtype(ct, q.dtype)
885
+ use_aligned_fastpath = _should_use_aligned_noncausal_fastpath(
886
+ seq_len=seq_len,
887
+ head_dim=head_dim,
888
+ causal=causal,
889
+ pad_dim=pad_dim,
890
+ chunk_plan=chunk_plan,
891
+ tile_m=tile_m,
892
+ tile_n=tile_n,
893
+ )
894
+ if chunk_plan is not None:
895
+ kernel_variant = "chunked"
896
+ elif use_aligned_fastpath:
897
+ kernel_variant = "direct_aligned_noncausal"
898
+ else:
899
+ kernel_variant = "direct"
900
+ kernel_key = (
901
+ tile_m,
902
+ tile_n,
903
+ kernel_head_dim,
904
+ kernel_variant,
905
+ str(q.dtype),
906
+ causal,
907
+ accum_mode,
908
+ opt_level,
909
+ occupancy if occupancy is not None else -1,
910
+ num_ctas if num_ctas is not None else -1,
911
+ )
912
+ if chunk_plan is None:
913
+ if use_aligned_fastpath:
914
+ kernel = get_kernel(
915
+ kernel_key,
916
+ lambda: make_flashattn_fwd_kernel_aligned_noncausal(
917
+ tile_m,
918
+ tile_n,
919
+ kernel_head_dim,
920
+ dtype=out_ct_dtype,
921
+ accum_mode=accum_mode,
922
+ opt_level=opt_level,
923
+ occupancy=occupancy,
924
+ num_ctas=num_ctas,
925
+ ),
926
+ )
927
+ else:
928
+ kernel = get_kernel(
929
+ kernel_key,
930
+ lambda: make_flashattn_fwd_kernel(
931
+ tile_m,
932
+ tile_n,
933
+ kernel_head_dim,
934
+ dtype=out_ct_dtype,
935
+ causal=causal,
936
+ accum_mode=accum_mode,
937
+ opt_level=opt_level,
938
+ occupancy=occupancy,
939
+ num_ctas=num_ctas,
940
+ ),
941
+ )
942
+ else:
943
+ kernel = get_kernel(
944
+ kernel_key,
945
+ lambda: make_flashattn_fwd_kernel_chunked(
946
+ tile_m,
947
+ tile_n,
948
+ kernel_head_dim,
949
+ chunk_plan=chunk_plan,
950
+ dtype=out_ct_dtype,
951
+ causal=causal,
952
+ accum_mode=accum_mode,
953
+ opt_level=opt_level,
954
+ occupancy=occupancy,
955
+ num_ctas=num_ctas,
956
+ ),
957
+ )
958
+
959
+ grid = (bh, (seq_len + tile_m - 1) // tile_m, 1)
960
+ if chunk_plan is None:
961
+ args = (q_bh, k_t, v_bh, out_bh, float(scale))
962
+ else:
963
+ (off0, w0), (off1, w1) = chunk_plan
964
+ if off0 != 0 or off1 != w0 or (w0 + w1) != head_dim:
965
+ raise ValueError(f"Unexpected chunk plan for head_dim={head_dim}: {chunk_plan!r}")
966
+ q0 = q_bh[:, :, :w0]
967
+ q1 = q_bh[:, :, w0:]
968
+ k0_t = k_bh[:, :, :w0].transpose(1, 2)
969
+ k1_t = k_bh[:, :, w0:].transpose(1, 2)
970
+ v0 = v_bh[:, :, :w0]
971
+ v1 = v_bh[:, :, w0:]
972
+ out0 = out_bh[:, :, :w0]
973
+ out1 = out_bh[:, :, w0:]
974
+ args = (q0, k0_t, v0, q1, k1_t, v1, out0, out1, float(scale))
975
+ _launch_cutile_kernel(ct, cupy_mod, kernel, grid, args)
976
+
977
+ out = out_bh[:, :, :head_dim] if pad_dim > 0 else out_bh
978
+ return out.reshape(batch, heads, seq_len, head_dim)
build/torch-cuda/metadata.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "tiledattention",
3
+ "id": "_tiledattention_cuda_703d09c_dirty",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda"
9
+ }
10
+ }
build/torch-cuda/sdpa.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PyTorch-facing SDPA API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from numbers import Real
7
+
8
+ try:
9
+ from . import _runtime
10
+ from ._errors import InvalidShapeError
11
+ from .kernels import run_flash_fwd
12
+ from .utils.checks import validate_sdpa_inputs
13
+ except ImportError: # pragma: no cover - flattened kernel package layout
14
+ import _runtime # type: ignore
15
+ from _errors import InvalidShapeError # type: ignore
16
+ from kernels import run_flash_fwd # type: ignore
17
+ from utils.checks import validate_sdpa_inputs # type: ignore
18
+
19
+
20
+ def sdpa(
21
+ q,
22
+ k,
23
+ v,
24
+ *,
25
+ causal: bool = False,
26
+ scale: float | None = None,
27
+ ):
28
+ """
29
+ Compute scaled dot-product attention.
30
+ It is part of the public SDPA execution path.
31
+
32
+ Args:
33
+ q: Query tensor in attention layout.
34
+ k: Key tensor in attention layout.
35
+ v: Value tensor in attention layout.
36
+ causal: Whether causal masking is enabled.
37
+ scale: Attention scaling factor.
38
+
39
+ Returns:
40
+ object: Function result value.
41
+ """
42
+
43
+ if not isinstance(causal, bool):
44
+ raise InvalidShapeError("causal must be a bool.")
45
+ if scale is not None and (not isinstance(scale, Real) or float(scale) <= 0):
46
+ raise InvalidShapeError("scale must be a positive number when provided.")
47
+
48
+ _runtime.require_supported_runtime()
49
+ torch_mod = _runtime.get_torch_module()
50
+ validate_sdpa_inputs(torch_mod, q, k, v)
51
+
52
+ head_dim = int(q.shape[-1])
53
+ resolved_scale = float(scale) if scale is not None else 1.0 / math.sqrt(head_dim)
54
+ return run_flash_fwd(q, k, v, causal=causal, scale=resolved_scale)
build/torch-cuda/tiledattention/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch-cuda/utils/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """Utility helpers for tiledattention."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .checks import validate_sdpa_inputs
6
+
7
+ __all__ = ["validate_sdpa_inputs"]
build/torch-cuda/utils/checks.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Input checks shared by tiledattention APIs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from types import ModuleType
6
+
7
+ try:
8
+ from .._errors import DTypeNotSupportedError, InvalidShapeError
9
+ except ImportError: # pragma: no cover - flattened kernel package layout
10
+ from _errors import DTypeNotSupportedError, InvalidShapeError # type: ignore
11
+
12
+
13
+ def _ensure_tensor(torch_mod: ModuleType, name: str, value: object) -> None:
14
+ """
15
+ Internal helper for ensure tensor.
16
+ It enforces input constraints before kernel execution.
17
+
18
+ Args:
19
+ torch_mod: Imported torch module instance.
20
+ name: Identifier or metric name.
21
+ value: Scalar value to parse or format.
22
+ """
23
+ if not isinstance(value, torch_mod.Tensor):
24
+ raise InvalidShapeError(f"{name} must be a torch.Tensor.")
25
+
26
+
27
+ def validate_sdpa_inputs(torch_mod: ModuleType, q, k, v) -> None:
28
+ """
29
+ Validate sdpa inputs.
30
+ It enforces input constraints before kernel execution.
31
+
32
+ Args:
33
+ torch_mod: Imported torch module instance.
34
+ q: Query tensor in attention layout.
35
+ k: Key tensor in attention layout.
36
+ v: Value tensor in attention layout.
37
+ """
38
+ _ensure_tensor(torch_mod, "q", q)
39
+ _ensure_tensor(torch_mod, "k", k)
40
+ _ensure_tensor(torch_mod, "v", v)
41
+
42
+ for name, tensor in (("q", q), ("k", k), ("v", v)):
43
+ if tensor.ndim != 4:
44
+ raise InvalidShapeError(f"{name} must have shape [B, H, S, D].")
45
+ if not bool(tensor.is_cuda):
46
+ raise InvalidShapeError(f"{name} must be a CUDA tensor.")
47
+
48
+ if q.shape != k.shape or q.shape != v.shape:
49
+ raise InvalidShapeError("q, k, and v must have matching shape [B, H, S, D].")
50
+
51
+ if q.device != k.device or q.device != v.device:
52
+ raise InvalidShapeError("q, k, and v must be on the same CUDA device.")
53
+
54
+ allowed_dtypes = {torch_mod.float16, torch_mod.bfloat16}
55
+ if q.dtype not in allowed_dtypes:
56
+ raise DTypeNotSupportedError("q dtype must be torch.float16 or torch.bfloat16.")
57
+ if q.dtype != k.dtype or q.dtype != v.dtype:
58
+ raise DTypeNotSupportedError("q, k, and v must have identical dtype.")