Re-do MLIR Python bindings hand-written over the C++ API (the eudsl-llvmpy way)
- Dominant language
- Python
- Stars
- 79
- Forks
- 14
- Avg merge
- 11h 43m
- Merged PRs (30d)
- 72
Description
`eudsl-py` (removed in #650) bound MLIR in Python by *auto-generating* nanobind bindings from MLIR C++ headers via `eudsl-nbgen` (a source-to-source translator driven mostly off TableGen-generated `*.h.inc` files). That approach turned out to be brittle and only translated a narrow slice of the API correctly.
This issue tracks doing MLIR Python bindings more correctly, applying **the `eudsl-llvmpy` approach where it fits**: hand-written `nanobind` wrappers over the core C++ API.
Note this is *not* an all-hand-written vs. all-generated choice. MLIR dialect ops are themselves auto-generated in C++ from TableGen (`mlir-tblgen` Op Definitions), so binding the dialects will necessarily involve **some form of autogeneration** too. The goal is to split the work correctly:
- **Core IR API — hand-written**, the way `eudsl-llvmpy` does LLVM IR / Machine IR (`Context`/`Module`/`Operation`/`Block`/`Region`/`Value`/`Type`/`Attribute`, etc.). This stable, hand-authored surface is what `eudsl-py`'s generated approach got wrong.
- **Dialect ops — generated**, since the C++ for them is already generated from TableGen. The open question is the *right* generation strategy (drive off the ODS/TableGen records or the generated headers, emit nanobind directly, etc.), not whether to generate at all.
The `eudsl-llvmpy` conventions we'd want to carry over to the hand-written core (see `projects/eudsl-llvmpy/CLAUDE.md`), and ideally have the generated dialect layer respect as well:
- **Mirror the C++ API.** Bind names as MLIR spells them; the Python class hierarchy mirrors the C++ one. Don't rename/merge methods because they read as equivalent.
- **Bind the full API; don't hardcode assumptions.** Surface the C++ parameters (defaults are fine) rather than baking one choice into C++.
- **Prefer specific nanobind types** over `nb::object`/`nb::handle`, so casts and identity checks are precise and `is` identity holds via nanobind's instance registry.
- **Breaking changes are fine** — no stability contract; evolve the API when it improves things and update tests/docs to match.
- **Tests + coverage discipline** as in eudsl-llvmpy (leak checks, per-PR C++ coverage over the binding sources).
Scope / open questions:
- Which MLIR surfaces to bind first (IR core listed above), and the boundary between the hand-written core and the generated dialect layer.
- The dialect-op generation strategy (source of truth: ODS/TableGen records vs. generated `*.h.inc`; what the emitted bindings look like) and how much can be shared with / learned from `eudsl-nbgen`.
- Relationship to the upstream MLIR Python bindings (`mlir-python-bindings`) — where this complements vs. overlaps.
Follow-up to #650.
Contributor guide
No contributing guide indexed for this repository
Research direction
Read projects/eudsl-llvmpy/CLAUDE.md and inspect its LLVM IR/Machine IR binding conventions first. Then map the MLIR core surfaces and dialect-generation boundary, comparing ODS/TableGen records, generated headers, and upstream MLIR Python bindings; done requires an agreed scope and strategy because the issue leaves these questions open.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- api, devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100