Skip to content

Add a public XLA FFI registration provider - #214

Merged
vbharadwaj-bk merged 7 commits into
PASSIONLab:mainfrom
tummfm:feature/jcn-ffi-provider
Sep 3, 2026
Merged

Add a public XLA FFI registration provider#214
vbharadwaj-bk merged 7 commits into
PASSIONLab:mainfrom
tummfm:feature/jcn-ffi-provider

Conversation

@pl-fuchs

@pl-fuchs pl-fuchs commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

This PR addresses the native FFI registration discussed in #213. I separated the XLA FFI handlers and shared handler table from the nanobind-specific registration, while keeping the existing JAX interface unchanged. I then added a Python-independent provider that registers the complete handler bundles through a stable C entry point. Any XLA-based application can now register the same OEQ-owned handlers using its local XLA_FFI_Api, without depending on nanobind or maintaining a duplicate handler list.

@vbharadwaj-bk vbharadwaj-bk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting set of changes, thanks!

The bulk of my review concentrated on the file now called ffi_handlers.cpp, and those diffs look fine. I'm not so familiar with XLA's FFI beyond the nanobind tutorial, but if it passes the build CI and the tests, I presume it's fine, and the code style also looks unobjectionable.

Comments / Questions

  1. Can you point us to some code or documentation on the FFI handler / table registration functions, etc. that you used? If we need to modify / maintain in the future, we will need some references to go off.

  2. We now have four files ffi_handler-table, ffi_handlers, ffi_provider, and ffi_registration. Can you provide some clarity about why the code was sectioned in this way?

  3. I presume that XLA's preferred build system is Bazel, hence the introduction of Bazel build file. I think this is fine if it is too difficult to make this an additional CMakeLists.txt target, but any comment on that? I suppose it doesn't hurt, and we do want this used in LAMMPS as painlessly as possible.

Other than these questions, seems good.

@vbharadwaj-bk vbharadwaj-bk added the ci-ready Triggers CI checks for a pull request label Sep 2, 2026
@pl-fuchs

pl-fuchs commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author
  1. The main reference I used is XLA's FFI C API:

https://github.com/openxla/xla/blob/main/xla/ffi/api/c_api.h

In particular, the implementation follows the staged handler model defined there (instantiate, prepare, initialize, and execute) and the native registration path through XLA_FFI_Handler_Bundle / XLA_FFI_Handler_Register.

The OEQ-side handler table is intentionally general enough to represent these stages rather than only the execute handler currently exposed through JAX. This change is also needed for the follow-up PR tummfm#1, which attaches compilation state to the XLA executable.

  1. The four files separate the handler implementation from the two different registration mechanisms.

ffi_handlers.cpp contains the actual OEQ FFI handler implementations and owns the table of handlers.

ffi_handler_table.h defines a small, OEQ-owned, Python-independent C ABI for exposing that table. It contains the handler name, the function pointers for the different XLA stages, and the handler traits. Both registration paths consume this same table, so there is only one authoritative list of handlers.

jax_ffi_registration.cpp implements the existing JAX-specific registration path. This part depends on nanobind and turns the handlers into the capsules expected by jax.ffi.register_ffi_target.

ffi_provider.cpp implements the native, Python-independent registration path. It consumes the same handler table and registers each handler bundle directly through XLA_FFI_Handler_Register using an XLA_FFI_Api supplied by the caller.

The intention of the separation is therefore to keep nanobind/JAX entirely out of the native XLA/PJRT path. The two registration mechanisms are disjoint, while the handlers themselves are shared.

  1. A CMake target for the standalone provider should also be possible. The exported provider interface is a C ABI, so the resulting shared library can be loaded independently of Python/nanobind.

The main reason I added the Bazel target first is my intended integration with chemtrain-deploy and its Bazel build path. It also allows the provider to build directly against the XLA and CUDA repositories selected by the consuming Bazel workspace.

@vbharadwaj-bk

Copy link
Copy Markdown
Member

Sounds reasonable, thanks for the quick responses, and CI passes. Am running the JAX test suite on my end overnight, should get back to you in a day.

@vbharadwaj-bk

Copy link
Copy Markdown
Member

https://github.com/PASSIONLab/OpenEquivariance/tree/jax_ffi_modifications

@pl-fuchs There are some errors for earlier JAX versions that were corrected by changing some of the ffi:Ffi function calls. With the modifications on the branch above, the test suite passes. Could you take a look and modify your branch accordingly? After that, this should be good to go.

@vbharadwaj-bk vbharadwaj-bk removed the ci-ready Triggers CI checks for a pull request label Sep 3, 2026
@pl-fuchs

pl-fuchs commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for catching this issue. I merged the fix and updated the branch.

@vbharadwaj-bk vbharadwaj-bk added the ci-ready Triggers CI checks for a pull request label Sep 3, 2026

@vbharadwaj-bk vbharadwaj-bk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vbharadwaj-bk
vbharadwaj-bk merged commit 8d76d26 into PASSIONLab:main Sep 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-ready Triggers CI checks for a pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants