llvm / llvm/eudsl

Re-do MLIR Python bindings hand-written over the C++ API (the eudsl-llvmpy way)

Open
#651 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.