Skip to content

LLVMParty/llvm-nanobind

Repository files navigation

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 .pyi stubs for IDE support
  • Tested: 25+ lit tests, 15 golden master test pairs

Installation

This package requires LLVM 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 .

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
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

# 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:

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

About

⚠️ WIP: LLVM-C Python bindings with nanobind.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors