ttsem 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ttsem/__init__.py +137 -0
- ttsem/__main__.py +92 -0
- ttsem/_compile_runner.py +65 -0
- ttsem/_examples/e15_11519_main_e1f944a7a.json +635 -0
- ttsem/_examples/e15_repro.py +48 -0
- ttsem/_fakedriver.py +129 -0
- ttsem/_guard.py +1045 -0
- ttsem/_install.py +26 -0
- ttsem/_interp_runner.py +85 -0
- ttsem/_judge_runner.py +791 -0
- ttsem/_tritonbench_runner.py +234 -0
- ttsem/bot.py +143 -0
- ttsem/check_fixtures.py +91 -0
- ttsem/defuse.py +109 -0
- ttsem/demo.py +334 -0
- ttsem/dump_corpus.py +60 -0
- ttsem/harness.py +1671 -0
- ttsem/inductor_trace.py +88 -0
- ttsem/interp.py +336 -0
- ttsem/interp2.py +146 -0
- ttsem/ir_types.py +93 -0
- ttsem/judge.py +1068 -0
- ttsem/judge_io.py +537 -0
- ttsem/judge_shapes.py +287 -0
- ttsem/judge_tritonbench.py +724 -0
- ttsem/layout_checks.py +285 -0
- ttsem/layouts.py +976 -0
- ttsem/linear_layout.py +676 -0
- ttsem/memory.py +372 -0
- ttsem/minimize.py +523 -0
- ttsem/mlir.py +1028 -0
- ttsem/ops.py +2634 -0
- ttsem/pass_stress.py +83 -0
- ttsem/pidraces.py +221 -0
- ttsem/py.typed +0 -0
- ttsem/pytest_ttsem.py +264 -0
- ttsem/races.py +1081 -0
- ttsem/races_corpus.py +132 -0
- ttsem/sanitize.py +1036 -0
- ttsem/torch_demo.py +130 -0
- ttsem/validate.py +425 -0
- ttsem/validate_corpus.py +118 -0
- ttsem/values.py +430 -0
- ttsem-0.1.0.dist-info/METADATA +386 -0
- ttsem-0.1.0.dist-info/RECORD +48 -0
- ttsem-0.1.0.dist-info/WHEEL +4 -0
- ttsem-0.1.0.dist-info/entry_points.txt +2 -0
- ttsem-0.1.0.dist-info/licenses/LICENSE +21 -0
ttsem/__init__.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""ttsem: an executable semantics of Triton's TTIR and TTGIR, and a per-pass validator.
|
|
2
|
+
|
|
3
|
+
The package has three layers, each usable on its own:
|
|
4
|
+
|
|
5
|
+
* **Level 1, values.** :func:`ttsem.mlir.parse` reads a generic-form MLIR module and
|
|
6
|
+
:class:`ttsem.interp.Interp` executes it over ``numpy`` arrays. No GPU, no Triton install,
|
|
7
|
+
no ``triton-opt``: the module text is all that is needed.
|
|
8
|
+
* **Level 2, layouts.** :class:`ttsem.interp2.LayoutInterp` and
|
|
9
|
+
:func:`ttsem.interp2.check_module` add the linear-layout model, so a TTGIR module can be
|
|
10
|
+
checked for layout violations without running it.
|
|
11
|
+
* **Level 3, concurrency.** :func:`ttsem.races.detect` replays one execution and reports
|
|
12
|
+
shared-memory races between the warps of a CTA.
|
|
13
|
+
|
|
14
|
+
On top of those, :func:`ttsem.validate.validate_stages` takes the modules a compilation
|
|
15
|
+
produced before every pass and names the first pass whose output the semantics reads
|
|
16
|
+
differently from the device, and :func:`ttsem.minimize.reduce` shrinks that module to the
|
|
17
|
+
smallest one on which the pass still changes the meaning.
|
|
18
|
+
|
|
19
|
+
Everything above level 3 needs a Triton install, because a launch has to be recorded and
|
|
20
|
+
compiled to get the stages in the first place; those names are imported lazily, so importing
|
|
21
|
+
``ttsem`` with nothing but ``numpy`` installed works and gives you the interpreter.
|
|
22
|
+
|
|
23
|
+
The whole-program entry point keeps its module name rather than being re-exported here, so
|
|
24
|
+
that ``from ttsem import validate`` is unambiguously the submodule: call
|
|
25
|
+
``ttsem.validate.validate(program, record)`` for it.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from typing import TYPE_CHECKING, Any
|
|
31
|
+
|
|
32
|
+
from ttsem.interp import Interp
|
|
33
|
+
from ttsem.interp2 import LayoutInterp, check_module, launch_config, module_attrs
|
|
34
|
+
from ttsem.ir_types import Block, Module, Op, Region, Type
|
|
35
|
+
from ttsem.memory import Memory, MemoryFault
|
|
36
|
+
from ttsem.mlir import NotGeneric, ParseError, parse, to_generic, unparse
|
|
37
|
+
from ttsem.races import detect
|
|
38
|
+
from ttsem.values import Poison, Unsupported, Value
|
|
39
|
+
|
|
40
|
+
__version__ = "0.1.0"
|
|
41
|
+
|
|
42
|
+
# The names below pull in `triton`, so they are resolved on first use rather than at import.
|
|
43
|
+
_LAZY = {
|
|
44
|
+
"DumpTooLarge": ("ttsem.harness", "DumpTooLarge"),
|
|
45
|
+
"LaunchRecord": ("ttsem.harness", "LaunchRecord"),
|
|
46
|
+
"capture_launches": ("ttsem.harness", "capture_launches"),
|
|
47
|
+
"compare": ("ttsem.harness", "compare"),
|
|
48
|
+
"dump_for_launch": ("ttsem.harness", "dump_for_launch"),
|
|
49
|
+
"execute": ("ttsem.harness", "execute"),
|
|
50
|
+
"ir_for_launch": ("ttsem.harness", "ir_for_launch"),
|
|
51
|
+
"run_launch": ("ttsem.harness", "run_launch"),
|
|
52
|
+
"PassResult": ("ttsem.validate", "PassResult"),
|
|
53
|
+
"Report": ("ttsem.validate", "Report"),
|
|
54
|
+
"capture_dump": ("ttsem.validate", "capture_dump"),
|
|
55
|
+
"culprit_of": ("ttsem.validate", "culprit_of"),
|
|
56
|
+
"split_dump": ("ttsem.validate", "split_dump"),
|
|
57
|
+
"validate_stages": ("ttsem.validate", "validate_stages"),
|
|
58
|
+
"reduce": ("ttsem.minimize", "reduce"),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if TYPE_CHECKING: # so that type checkers and editors see the same API
|
|
62
|
+
from ttsem.harness import (
|
|
63
|
+
DumpTooLarge,
|
|
64
|
+
LaunchRecord,
|
|
65
|
+
capture_launches,
|
|
66
|
+
compare,
|
|
67
|
+
dump_for_launch,
|
|
68
|
+
execute,
|
|
69
|
+
ir_for_launch,
|
|
70
|
+
run_launch,
|
|
71
|
+
)
|
|
72
|
+
from ttsem.minimize import reduce
|
|
73
|
+
from ttsem.validate import (
|
|
74
|
+
PassResult,
|
|
75
|
+
Report,
|
|
76
|
+
capture_dump,
|
|
77
|
+
culprit_of,
|
|
78
|
+
split_dump,
|
|
79
|
+
validate_stages,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def __getattr__(name: str) -> Any:
|
|
84
|
+
try:
|
|
85
|
+
module_name, attr = _LAZY[name]
|
|
86
|
+
except KeyError:
|
|
87
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
88
|
+
import importlib
|
|
89
|
+
|
|
90
|
+
value = getattr(importlib.import_module(module_name), attr)
|
|
91
|
+
globals()[name] = value
|
|
92
|
+
return value
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def __dir__() -> list[str]:
|
|
96
|
+
return sorted(set(globals()) | set(_LAZY))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
"Block",
|
|
101
|
+
"DumpTooLarge",
|
|
102
|
+
"Interp",
|
|
103
|
+
"LaunchRecord",
|
|
104
|
+
"LayoutInterp",
|
|
105
|
+
"Memory",
|
|
106
|
+
"MemoryFault",
|
|
107
|
+
"Module",
|
|
108
|
+
"NotGeneric",
|
|
109
|
+
"Op",
|
|
110
|
+
"ParseError",
|
|
111
|
+
"PassResult",
|
|
112
|
+
"Poison",
|
|
113
|
+
"Region",
|
|
114
|
+
"Report",
|
|
115
|
+
"Type",
|
|
116
|
+
"Unsupported",
|
|
117
|
+
"Value",
|
|
118
|
+
"__version__",
|
|
119
|
+
"capture_dump",
|
|
120
|
+
"capture_launches",
|
|
121
|
+
"check_module",
|
|
122
|
+
"compare",
|
|
123
|
+
"culprit_of",
|
|
124
|
+
"detect",
|
|
125
|
+
"dump_for_launch",
|
|
126
|
+
"execute",
|
|
127
|
+
"ir_for_launch",
|
|
128
|
+
"launch_config",
|
|
129
|
+
"module_attrs",
|
|
130
|
+
"parse",
|
|
131
|
+
"reduce",
|
|
132
|
+
"run_launch",
|
|
133
|
+
"split_dump",
|
|
134
|
+
"to_generic",
|
|
135
|
+
"unparse",
|
|
136
|
+
"validate_stages",
|
|
137
|
+
]
|
ttsem/__main__.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""``python -m ttsem``: what the package can do, and which module does it."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from ttsem import __version__
|
|
8
|
+
|
|
9
|
+
USAGE = f"""ttsem {__version__} - an executable semantics of Triton's TTIR and TTGIR.
|
|
10
|
+
|
|
11
|
+
The package is a library first; every entry point below is a module you run with `-m`.
|
|
12
|
+
After a pip install, the `ttsem` command is the same as `python -m ttsem`.
|
|
13
|
+
|
|
14
|
+
python -m ttsem demo [--live]
|
|
15
|
+
The whole idea on one real bug (triton#11519): replay the committed recording of a
|
|
16
|
+
per-pass validation, or, with --live and the Triton wheel, redo it here. No GPU.
|
|
17
|
+
|
|
18
|
+
python -m ttsem demo --torch
|
|
19
|
+
Races in the kernels torch.compile generates (pytorch#197829, #198033), found by
|
|
20
|
+
running Inductor's Triton code under the semantics. Needs torch; no GPU.
|
|
21
|
+
|
|
22
|
+
python -m ttsem judge TASK.py ANSWER.py [--json] [--rule kernelbench|v3]
|
|
23
|
+
python -m ttsem judge --manifest M.jsonl --out DIR [--jobs N]
|
|
24
|
+
A KernelBench answer written with Triton, judged by KernelBench's own correctness
|
|
25
|
+
rule with every launch executed by the semantics: verified, wrong, unsafe (with the
|
|
26
|
+
kernel's line), no_kernel (no Triton kernel launched), error, too_slow or not_judged.
|
|
27
|
+
Each answer runs in its own bwrap sandbox. Needs torch and Triton; no GPU.
|
|
28
|
+
With --tritonbench, a TritonBench answer against its task's reference, by value.
|
|
29
|
+
|
|
30
|
+
python -m ttsem.validate PROGRAM.py [--device cpu|cuda] [--json OUT]
|
|
31
|
+
Compile PROGRAM.py's first launch with the dump on and run every pass's input
|
|
32
|
+
through the semantics. Names the first pass whose output the semantics reads
|
|
33
|
+
differently from the device. Needs Triton.
|
|
34
|
+
|
|
35
|
+
python -m ttsem.minimize PROGRAM.py [--stage PASS] [--out small.mlir]
|
|
36
|
+
Shrink that module to the smallest one on which the pass still changes the meaning.
|
|
37
|
+
|
|
38
|
+
python -m ttsem.sanitize pytest [PYTEST ARGS] | SCRIPT.py
|
|
39
|
+
Run a test suite or a script written for a GPU on a machine without one: every
|
|
40
|
+
launch is executed by the semantics, and an out-of-bounds access, a race between
|
|
41
|
+
program instances or an operation with an undefined result fails the test with the
|
|
42
|
+
kernel's own line.
|
|
43
|
+
|
|
44
|
+
python -m ttsem.races MODULE.generic.mlir [--strip-barriers]
|
|
45
|
+
Shared-memory races between the warps of a CTA, from one execution of the module.
|
|
46
|
+
Needs no Triton and no GPU.
|
|
47
|
+
|
|
48
|
+
python -m ttsem.check_fixtures [--dir DIR] [--pattern GLOB]
|
|
49
|
+
Level-2 layout checks over a directory of generic-form TTGIR dumps. No Triton.
|
|
50
|
+
|
|
51
|
+
python -m ttsem.bot summary RUN/ | diff PREVIOUS/ CURRENT/
|
|
52
|
+
Bookkeeping over the JSONL a validated test run writes.
|
|
53
|
+
|
|
54
|
+
pytest -p ttsem.pytest_ttsem ... validate every launch of a test run
|
|
55
|
+
python -m ttsem.inductor_trace --out DIR SCRIPT.py
|
|
56
|
+
the same for torch.compile's kernels
|
|
57
|
+
python -m ttsem.validate_corpus --programs DIR --results OUT
|
|
58
|
+
python -m ttsem.pass_stress --programs DIR which passes a corpus actually wakes
|
|
59
|
+
python -m ttsem.dump_corpus --programs DIR --out DIR
|
|
60
|
+
|
|
61
|
+
In Python:
|
|
62
|
+
|
|
63
|
+
from ttsem import parse, Interp, Memory # level 1, numpy only
|
|
64
|
+
from ttsem import check_module # level 2, layouts
|
|
65
|
+
from ttsem import detect # level 3, races
|
|
66
|
+
from ttsem import validate_stages, culprit_of # validate_stages needs Triton, culprit_of does not
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main(argv: list[str] | None = None) -> int:
|
|
71
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
72
|
+
if argv and argv[0] == "demo":
|
|
73
|
+
from ttsem.demo import main as demo_main
|
|
74
|
+
|
|
75
|
+
return demo_main(argv[1:])
|
|
76
|
+
if argv and argv[0] == "judge":
|
|
77
|
+
from ttsem.judge import main as judge_main
|
|
78
|
+
|
|
79
|
+
return judge_main(argv[1:])
|
|
80
|
+
if argv and argv[0] in ("-h", "--help"):
|
|
81
|
+
sys.stdout.write(USAGE)
|
|
82
|
+
return 0
|
|
83
|
+
if argv:
|
|
84
|
+
sys.stderr.write(f"unknown command {argv[0]!r}\n\n")
|
|
85
|
+
sys.stderr.write(USAGE)
|
|
86
|
+
return 2
|
|
87
|
+
sys.stdout.write(USAGE)
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
raise SystemExit(main())
|
ttsem/_compile_runner.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Subprocess entry point for ``validate.py``'s ``MLIR_ENABLE_DUMP`` capture without a GPU.
|
|
2
|
+
|
|
3
|
+
Installs the fake CUDA driver (:func:`ttsem.harness._install_fake_driver`) and
|
|
4
|
+
replaces ``JITFunction.run`` so every launch is compiled for that fake target -- which is what
|
|
5
|
+
actually produces the ``MLIR_ENABLE_DUMP`` pass-by-pass text on stderr, the whole point of
|
|
6
|
+
running this -- and never executed: there is no GPU here to launch a real kernel on.
|
|
7
|
+
|
|
8
|
+
Not meant to be imported: run as ``python -m ttsem._compile_runner PROGRAM.py --cc CC``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from triton.backends.compiler import GPUTarget
|
|
19
|
+
from triton.runtime.jit import JITFunction
|
|
20
|
+
|
|
21
|
+
from ttsem import harness
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _install(target: GPUTarget) -> None:
|
|
25
|
+
def run(self: JITFunction, *args: Any, grid: Any, warmup: bool, **kwargs: Any) -> Any:
|
|
26
|
+
if not warmup:
|
|
27
|
+
try:
|
|
28
|
+
harness._compile_asm(self, args, kwargs, target)
|
|
29
|
+
except Exception as e: # the dump on stderr is what matters, not this return value
|
|
30
|
+
print(f"[_compile_runner] compile failed: {e!r}", file=sys.stderr)
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
JITFunction.run = run
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main() -> int:
|
|
37
|
+
ap = argparse.ArgumentParser()
|
|
38
|
+
ap.add_argument("program", type=Path)
|
|
39
|
+
ap.add_argument("--cc", type=int, default=harness.DEFAULT_CC)
|
|
40
|
+
ap.add_argument("--device", choices=("cpu", "cuda"), default="cpu")
|
|
41
|
+
ap.add_argument(
|
|
42
|
+
"--generic",
|
|
43
|
+
action="store_true",
|
|
44
|
+
help="switch the wheel's printer to the generic form, so the dump needs no triton-opt",
|
|
45
|
+
)
|
|
46
|
+
args = ap.parse_args()
|
|
47
|
+
|
|
48
|
+
if args.generic:
|
|
49
|
+
from ttsem import mlir
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
mlir.enable_generic_printing()
|
|
53
|
+
except RuntimeError as exc: # a source build hides the symbol: the dump comes pretty
|
|
54
|
+
print(f"ttsem: {exc}", file=sys.stderr)
|
|
55
|
+
if args.device == "cuda":
|
|
56
|
+
harness._run_program(args.program, "cuda")
|
|
57
|
+
return 0
|
|
58
|
+
harness._install_fake_driver(args.cc)
|
|
59
|
+
_install(harness.target_for("cpu", args.cc))
|
|
60
|
+
harness._run_program(args.program, "cpu")
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
sys.exit(main())
|