llvm-nanobind
Python bindings for the LLVM-C API using nanobind.
This project provides a Pythonic interface to LLVM's compiler infrastructure, enabling you to build compilers, analyzers, and code transformation tools in Python.
Status: Under active development. Core APIs are bound and tested against LLVM's llvm-c-test suite, but the API is not yet stable. Expect breaking changes.
Note: This project is 90%+ vibe coded. It is mostly an experiment to see what LLMs can do when you set things up properly.
Features
- Comprehensive LLVM-C API coverage (~7300 lines of bindings)
- Memory-safe: validity tokens prevent use-after-free crashes
- Type-safe: auto-generated
.pyistubs for IDE support - Tested: 25+ lit tests, 15 golden master test pairs
Installation
This package requires LLVM 21+ to be installed. The build will automatically find LLVM if it's in your PATH, or you can specify the path:
export CMAKE_PREFIX_PATH=/path/to/llvm
pip install .
See llvm-nanobind-example for a simple example project.
Quick Start
import llvm
# Create a simple function that returns 42
with llvm.create_context() as ctx:
with ctx.create_module("example") as mod:
# Create function type: i32 ()
i32 = ctx.int32_type()
fn_type = ctx.function_type(i32, [])
# Create function and basic block
fn = mod.add_function("get_answer", fn_type)
bb = fn.append_basic_block("entry")
# Build return instruction
with ctx.create_builder() as builder:
builder.position_at_end(bb)
builder.ret(llvm.const_int(i32, 42))
# Print the IR
print(mod)
Development
Setup
# Configure (first time)
cmake -B build -G Ninja
# Build
cmake --build build
# Or use uv (recommended) - auto-rebuilds as needed
uv sync
# Offline build
uv sync --offline --no-build-isolation --verbose
Testing
# Main golden-master suite:
# - runs C++ test executables from build/
# - runs paired Python scripts
# - compares Python output against stored C++ behavior
uv run run_tests.py
# Python-only regression scripts in tests/regressions/
uv run run_tests.py --regressions
# Vendored llvm-c-test lit suite against the C test binary
# Rebuilds the vendored C binary before running lit.
uv run run_llvm_c_tests.py
uv run run_llvm_c_tests.py -v
# Vendored llvm-c-test lit suite against the Python implementation
uv run run_llvm_c_tests.py --use-python
# Run the Python llvm-c-test port directly during development
uv run python -m llvm_c_test --targets-list
# Type checking (not a test suite, but commonly run in CI/dev)
uvx ty check
If you want the closest thing to “run everything in this repo”, use:
uv run run_tests.py
uv run run_tests.py --regressions
uv run run_llvm_c_tests.py
uv run run_llvm_c_tests.py --use-python
Python tests here are intended to be executable as standalone scripts
(e.g. uv run tests/test_module.py or uv run tests/regressions/test_const_bytes.py).
They are generally pytest-compatible too, but direct script execution is the
historical/default style used by run_tests.py and for one-off debugging.
pytest is still useful for targeting specific regression files or subsets, but
it is not our complete top-level test entrypoint by itself.
Coverage
# Run with coverage
uv run coverage run run_llvm_c_tests.py --use-python
uv run coverage combine
uv run coverage report --include="llvm_c_test/*"
Documentation
Type stubs are auto-generated and provide IDE intellisense. After building, find them at:
.venv/lib/python3.*/site-packages/llvm/__init__.pyi
For development documentation, see devdocs/README.md.
License
This project is licensed under the MIT License. See LICENSE for details.
LLVM is licensed under the Apache License v2.0 with LLVM Exceptions.
Windows
Download LLVM+Clang:
- https://github.com/vovkos/llvm-package-windows/releases/download/llvm-21.1.1/llvm-21.1.1-windows-amd64-msvc17-msvcrt.7z
- https://github.com/vovkos/llvm-package-windows/releases/download/clang-20.1.8/clang-20.1.8-windows-amd64-msvc17-msvcrt.7z
Merge them together in C:\llvm-21.1.1.
Create CMakeUserPresets.json:
{
"version": 3,
"configurePresets": [
{
"name": "clang-cl",
"displayName": "Ninja with clang-cl",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_C_COMPILER": "C:/Program Files/LLVM/bin/clang-cl.exe",
"CMAKE_CXX_COMPILER": "C:/Program Files/LLVM/bin/clang-cl.exe",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CMAKE_PREFIX_PATH": "d:/llvm-21.1.1"
}
}
],
"buildPresets": [
{
"name": "clang-cl",
"configurePreset": "clang-cl"
}
]
}
Create a virtual environment:
uv venv
Activate the virtual environment:
.venv/Scripts/activate
Configure the CMake project (this should find the Python from your venv):
cmake --preset clang-cl
Note: This saves the LLVM prefix to a file called .llvm-prefix, make sure to delete that if you change the LLVM prefix path.
Build the bindings:
cmake --build build
After that works you can build the Python package with uv:
uv sync --verbose