CLAUDE.md
torch/_dynamo/CLAUDE.mdCLAUDE.md
Quality
84/100
Scores the file, not the repository.Length
1,380 words
23 headings · 9 code blocksRepository
102k
— · pushed 0 days agoLast changed
3 days ago
First indexed 3 days ago.1# torch/_dynamo23TorchDynamo is a Python-level JIT compiler that captures PyTorch programs into4FX graphs by symbolically executing Python bytecode. It hooks into CPython's5PEP 523 frame evaluation API to intercept execution, traces operations into an6FX graph, compiles the graph with a backend (e.g. Inductor), and generates new7bytecode that calls the compiled code.89## Architecture Overview1011The compilation pipeline, in execution order:12131. **`eval_frame.py`** — Runtime entry point. `torch.compile()` wraps a14 function in an `OptimizedModule`. At runtime, the C extension15 (`torch._C._dynamo.eval_frame`) intercepts Python frames via PEP 523.162. **`convert_frame.py`** — `ConvertFrameAssert.__call__` checks caches,17 handles recompilation limits, calls `_compile()` → `trace_frame()`.183. **`symbolic_convert.py`** — The heart of Dynamo. `InstructionTranslator`19 symbolically executes bytecode instruction-by-instruction. Maintains a20 symbolic `stack` (list of `VariableTracker`s) and `symbolic_locals` (dict of21 name → `VariableTracker`). Opcodes are dispatched via a `dispatch_table`22 built by `BytecodeDispatchTableMeta`.234. **`output_graph.py`** — `OutputGraph` owns the FX graph being built (via24 `SubgraphTracer`), the `SideEffects` tracker, guards, shape environment,25 and graph args. `compile_subgraph()` finalizes the graph, calls the backend,26 and generates output bytecode.275. **`codegen.py`** — `PyCodegen` emits output bytecode: loads graph inputs28 (via `Source.reconstruct()`), calls the compiled graph, unpacks outputs, and29 replays side effects.306. **`resume_execution.py`** — Generates continuation functions for execution31 after graph breaks.3233## Key Abstractions3435### VariableTracker (`variables/`)3637Every Python value encountered during tracing is wrapped in a `VariableTracker`38subclass. Key interface: `as_python_constant()`, `as_proxy()`,39`call_function()`, `call_method()`, `getattro_impl()`, `reconstruct()`.4041Key fields: `source` (where the value came from, for guards) and42`mutation_type` (whether/how mutations are tracked).4344**Factory**: `VariableTracker.build(tx, value, source=...)` dispatches to45`VariableBuilder` (sourced values needing guards) or `SourcelessBuilder`46(values created during tracing).4748Key subclass families in `variables/`: `TensorVariable` / `SymNodeVariable`49(tensor.py), `ConstantVariable` (constant.py), `ListVariable` /50`TupleVariable` (lists.py), `ConstDictVariable` (dicts.py), `SetVariable` (sets.py),51`UserFunctionVariable` (functions.py), `BuiltinVariable` (builtin.py),52`NNModuleVariable` (nn_module.py), `UserDefinedObjectVariable`53(user_defined.py), `TorchHigherOrderOperatorVariable` (higher_order_ops.py),54`LazyVariableTracker` (lazy.py). `VariableBuilder` and `SourcelessBuilder` are55in builder.py.5657### Source (`source.py`)5859Tracks value provenance — how to access a value at runtime. Used for guard60generation (`source.make_guard(GuardBuilder.XXX)`) and bytecode reconstruction61(`source.reconstruct(codegen)`). Root sources: `LocalSource`, `GlobalSource`.62Chained sources: `AttrSource`, `GetItemSource`, `NNModuleSource`, etc.6364### Guards (`guards.py`)6566Runtime conditions that must hold for cached compiled code to be reused.67Install via `install_guard(source.make_guard(GuardBuilder.TYPE_MATCH))`.68Common types: `TYPE_MATCH`, `ID_MATCH`, `EQUALS_MATCH`, `TENSOR_MATCH`,69`SEQUENCE_LENGTH`. At finalization, `CheckFunctionManager` builds a tree of70C++ `GuardManager` objects for fast runtime checking.7172### Side Effects (`side_effects.py`)7374Tracks mutations during tracing (attribute stores, list mutations, cell75variable updates, tensor hooks) and replays them as bytecode after graph76execution. The `MutationType` system (`variables/base.py`) controls what77mutations are allowed: `ValueMutationNew/Existing`,78`AttributeMutationNew/Existing`, or `None` (immutable). The `scope` field79prevents cross-scope mutations inside higher-order operators.8081### Other key files8283- `trace_rules.py` — inline/skip/graph-break decisions per function84- `exc.py` — exception hierarchy: `Unsupported` (graph break), `RestartAnalysis`85 (restart tracing), `ObservedException` (user exceptions during tracing),86 `BackendCompilerFailed`87- `config.py` — configuration flags, supports `config.patch()` context88 manager/decorator89- `bytecode_transformation.py` / `bytecode_analysis.py` — low-level bytecode90 manipulation, liveness analysis91- `pgo.py` — profile-guided optimization for dynamic shapes92- `polyfills/` — traceable replacements for stdlib functions93- `repro/` — reproduction/minification tools9495## Graph Breaks9697Call `unimplemented()` (from `exc.py`) to trigger a graph break:9899```python100from torch._dynamo.exc import unimplemented101from torch._dynamo import graph_break_hints102103unimplemented(104 gb_type="short_category_name",105 context=f"dynamic details: {value}",106 explanation="Human-readable explanation of why this breaks the graph.",107 hints=[*graph_break_hints.SUPPORTABLE],108)109```110111- `gb_type`: Context-free category (no dynamic strings).112- `context`: Developer-facing details (can be dynamic).113- `explanation`: User-facing explanation (can be dynamic).114- `hints`: Use constants from `graph_break_hints.py`: `SUPPORTABLE`,115 `FUNDAMENTAL`, `DIFFICULT`, `DYNAMO_BUG`, `USER_ERROR`,116 `CAUSED_BY_EARLIER_GRAPH_BREAK`.117118The `break_graph_if_unsupported` decorator on instruction handlers catches119`Unsupported`, logs the graph break, updates the `SpeculationLog`, and restarts120analysis. On the second pass, the partial graph is compiled at the break point121and a resume function handles the rest.122123## Testing124125Tests live in `test/dynamo/`. Use `torch._dynamo.test_case.TestCase` as base126class — it calls `torch._dynamo.reset()` in setUp/tearDown and patches config127for strict error checking.128129```bash130python test/dynamo/test_misc.py # whole file131python test/dynamo/test_misc.py MiscTests.test_foo # single test132python test/dynamo/test_misc.py -k test_foo # pattern match133```134135### Common patterns136137The default backend to `torch.compile()` is `backend="eager"`.138139**CompileCounter** — count compilations and graph ops:140```python141cnt = torch._dynamo.testing.CompileCounter()142143@torch.compile(backend=cnt)144def fn(x):145 return x + 1146147fn(torch.randn(10))148self.assertEqual(cnt.frame_count, 1)149self.assertEqual(cnt.op_count, 1)150```151152**fullgraph=True** — assert no graph breaks:153```python154torch.compile(fn, backend="eager", fullgraph=True)(x)155```156157**EagerAndRecordGraphs** — inspect captured FX graphs:158```python159backend = torch._dynamo.testing.EagerAndRecordGraphs()160torch.compile(fn, backend=backend)(x)161graph = backend.graphs[0]162```163164**normalize_gm + assertExpectedInline** — snapshot test graph output:165```python166from torch._dynamo.testing import normalize_gm167self.assertExpectedInline(168 normalize_gm(backend.graphs[0].print_readable(False)),169 """\170expected output here171""",172)173```174175Call `torch._dynamo.reset()` within a test when testing multiple compilation176scenarios in a single test method. The base class handles setUp/tearDown reset177automatically.178179## Debugging180181### TORCH_LOGS182183```bash184TORCH_LOGS="graph_breaks" python script.py # see graph breaks185TORCH_LOGS="guards,recompiles" python script.py # see guards and recompilation reasons186TORCH_LOGS="graph_code" python script.py # see captured FX graph code187TORCH_LOGS="+dynamo" python script.py # full debug logging188TORCH_LOGS="bytecode" python script.py # see bytecode transformations189```190191### Reproducing crashes192193When writing a minimal repro for a Dynamo crash:1941951. **Capture `TORCH_LOGS="+dynamo"` for the known-failing case.** Look for196 the frame ID (e.g. `[3/0_1]`), INLINING/FAILED/COMPILING/Restart events,197 speculation behavior, and graph break reasons.1982. **Capture the same logs for your repro attempt** and diff against the199 failing case. The divergence point tells you what condition you're missing.2003. **Match each condition from the crash traceback:**201 - Does the code need a speculation checkpoint? Add tensor ops before the202 failing code so `compile_subgraph` creates one.203 - Does the function need to be inlined on retry (not skipped)? The graph204 break type matters — `step_unsupported` keeps inlining on retry while205 `unimplemented` may skip the function.206 - Does the crash happen in a resume function? Add a `graph_break()` earlier207 so PEP 523 compiles the resume as a fresh frame.208209### Structured tracing (for production)210211```bash212TORCH_TRACE=/path/to/dir python script.py # explicit trace directory213```214215Analyze with `tlparse`.216217### Compile-time profiling218219`TORCH_COMPILE_DYNAMO_PROFILER=1` prints per-function cumtime/tottime220(cProfile-style) showing where Dynamo spends time during tracing. Set to a221file path instead to save a profile loadable by `snakeviz`.222223### comptime.breakpoint() (`comptime.py`)224225Drops into pdb during **compilation** to inspect Dynamo state. Call226`comptime.breakpoint()` in user code; in the pdb session use `ctx`227(`ComptimeContext`) to call `print_locals()`, `print_bt()`, `print_graph()`,228or `get_local("x").as_fake()`.229230### Bytecode Debugger (`bytecode_debugger.py`)231232pdb-like debugger for stepping through Dynamo-generated bytecode. Useful for233debugging segfaults (no Python traceback) and codegen errors.234235```python236with torch._dynamo.bytecode_debugger.debug():237 my_compiled_fn(x)238```239240**Programmatic breakpoints** (no graph break): call241`torch._dynamo.bytecode_debugger.breakpoint()` in user code, or242`codegen.extend_output(create_breakpoint())` in codegen. Auto-activates243without an explicit `debug()` wrapper.244245**Segfault debugging**: `v` (verbose) then `c` (continue) — every instruction246is printed with `flush=True` before execution, so the last line before a crash247is the culprit. On exceptions, the debugger stops at the faulting instruction248automatically.249250## C++ Runtime (`torch/csrc/dynamo/`)251252The C/C++ layer implements the PEP 523 frame evaluation hook, the cache, and253the guard evaluation tree. Performance-critical runtime on every Python frame.254255### Frame Evaluation256257**`eval_frame.c`** — Installs a custom frame evaluation function via258`_PyInterpreterState_SetEvalFrameFunc`. A thread-local callback controls259behavior: `None` (disabled), `Py_False` (run-only / cache lookup), or a260callable (full Dynamo).261262**`eval_frame_cpp.cpp`** — `dynamo__custom_eval_frame` is called for every263frame: gets `ExtraState` from the code object, builds a `FrameLocalsMapping`264(O(1) access to locals without dict materialization), evaluates guards via265`run_root_guard_manager()` across all `CacheEntry`s (LRU ordered). On cache266hit, executes compiled code via a shadow frame (`dynamo_eval_custom_code_impl`267copies `localsplus` into a new frame with the compiled code object). On miss,268calls the Python callback to trigger compilation.269270### Cache271272**`extra_state.cpp/.h`** — `ExtraState` is attached per code object via273`_PyCode_SetExtra`. Contains a `cache_entry_list` (LRU linked list),274`frame_state` (dynamic shapes detection), and `FrameExecStrategy`.275276**`cache_entry.cpp/.h`** — Each `CacheEntry` stores a `RootGuardManager*`277(raw C++ pointer for fast guard eval), the compiled code object, and the278backend.279280### Guard Evaluation Tree (`guards.cpp`)281282Guards are organized as a C++ tree (~7800 lines) mirroring the data access283pattern. `RootGuardManager` is the root, receiving a `FrameLocalsMapping`.284Each `GuardManager` node has leaf guards and child accessors.285286**LeafGuard** subclasses: `TYPE_MATCH` (Py_TYPE pointer comparison),287`ID_MATCH`, `EQUALS_MATCH`, `TENSOR_MATCH` (dtype/device/shape/strides/dispatch288keys in C++), `DICT_VERSION`, `GLOBAL_STATE` (grad mode, autocast, etc.).289290**GuardAccessor** subclasses define tree edges: `FrameLocalsGuardAccessor`291(O(1) index), `GetAttrGuardAccessor`, `DictGetItemGuardAccessor`,292`GlobalsGuardAccessor`, etc.293294Key optimizations: fail-fast accessor reordering, dict version tag matching to295skip subtrees, `FrameLocalsMapping` avoids dict construction,296`check_nopybind()` avoids pybind11 overhead.297298### Other C++ files299300- `framelocals_mapping.cpp` — O(1) frame locals/cells/freevars access301- `cpython_defs.c` — copied CPython internals for frame manipulation302- `init.cpp` — `torch._C._dynamo` module and pybind11 bindings303- `debug_macros.h` — `DEBUG_TRACE`, `NULL_CHECK`, `INSPECT(...)` (drops into304 pdb from C)305- `compiled_autograd.cpp/.h` — compiled autograd engine306
Also in pytorch/pytorch
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| pytorch/pytorch.github/copilot-instructions.md · 102k | Copilot instructions | setupbuildteststyle+5 | 100/100 | 3 days ago | |
| pytorch/pytorchCLAUDE.md · 102k | CLAUDE.md | setupbuildtestlint-format+5 | 88/100 | 3 days ago |
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Adit-Jain-srm/NightmareNetCLAUDE.md · 45 | CLAUDE.md | buildtestlint-formatstyle+6 | 100/100 | 3 days ago | |
| dotCMS/corecore-web/CLAUDE.md · 950 | CLAUDE.md | teststylearchtesting-strategy+3 | 100/100 | 3 days ago | |
| dotCMS/coreCLAUDE.md · 950 | CLAUDE.md | setupbuildteststyle+7 | 99/100 | 3 days ago | |
| supabase/supabase.claude/CLAUDE.md · 107k | CLAUDE.md | testlint-formatstylearch+1 | 97/100 | 3 days ago | |
| dotCMS/corecore-web/libs/sdk/react/CLAUDE.md · 950 | CLAUDE.md | setupbuildtestlint-format+9 | 97/100 | 3 days ago | |
| dotCMS/corecore-web/libs/sdk/client/CLAUDE.md · 950 | CLAUDE.md | setupbuildtestlint-format+9 | 97/100 | 3 days ago | |
| modelcontextprotocol/serversCLAUDE.md · 89k | CLAUDE.md | setupbuildtestlint-format+6 | 97/100 | 3 days ago | |
| luongnv89/claude-howtovi/CLAUDE.md · 41k | CLAUDE.md | setupbuildtestlint-format+8 | 97/100 | 3 days ago |
