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 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())
@@ -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())