LLVMParty/llvm-nanobind
GitHub: LLVMParty/llvm-nanobind
该项目提供基于 nanobind 的 LLVM-C API Python 绑定,使开发者能在 Python 中构建编译器、分析器和代码转换工具。
Stars: 22 | Forks: 3
# llvm-nanobind
使用 [nanobind](https://github.com/wjakob/nanobind) 实现的 LLVM-C API 的 Python 绑定。
本项目为 LLVM 的编译器基础设施提供了一个符合 Python 风格的接口,使您能够使用 Python 构建编译器、分析器和代码转换工具。
**当前状态**:处于实验性阶段但可用。此绑定针对 LLVM 21.1.6,并已在 PyPI 上发布为 `llvm-nanobind`(导入名称为 `llvm`)。Wheel 包中捆绑了 LLVM,并在 CI 中针对 Linux x86_64/aarch64、macOS arm64 和 Windows x86_64 进行了构建/测试。API 仍在不断演进中;在发布稳定版本之前,预计会有破坏性变更。
版本号与 LLVM 保持同步:`21.1.6.1` 表示 LLVM `21.1.6` 加上绑定/包的修订版本 `1`。
_注_:本项目 90% 以上的代码是通过“氛围编程”完成的。这主要是一项实验,旨在探索在正确配置环境的情况下,LLM 能够做到什么程度。
## 安装说明
已发布的 Wheel 包针对受支持的平台捆绑了 LLVM,因此常规安装命令为:
```
pip install llvm-nanobind
python -c "import llvm; print(llvm)"
```
有关源码/开发构建,请参阅[开发](#development)。
有关简单的示例项目,请参阅[llvm-nanobind-example](https://github.com/LLVMParty/llvm-nanobind-example)。
## 快速入门
```
import llvm
# 创建一个返回 42 的简单函数。
with llvm.create_context() as ctx:
i32 = ctx.types.i32
fn_type = ctx.types.function(i32, [])
with ctx.create_module("example") as mod:
fn = mod.add_function("get_answer", fn_type)
entry = fn.append_basic_block("entry")
with entry.create_builder() as builder:
builder.ret(i32.constant(42))
assert mod.verify(), mod.verification_error
print(mod)
```
此确切代码片段可在 [`examples/quick_start.py`](examples/quick_start.py) 中找到。
## 示例
可运行的示例位于 [`examples/`](examples/) 目录中,并由 `tests/test_examples.py` 测试覆盖。
```
uv run python examples/quick_start.py
uv run python examples/transform_replace_add.py
```
其他值得浏览的示例:
- [`examples/intrinsic_memcpy.py`](examples/intrinsic_memcpy.py) - 使用 `Builder.intrinsic(...)` 按名称调用 LLVM 内联函数
- [`examples/optimize_module.py`](examples/optimize_module.py) - 使用 PassBuilder 流水线字符串优化 module
- [`examples/optimize_function.py`](examples/optimize_function.py) - 使用函数级别的 PassBuilder 流水线字符串优化单个函数
- [`examples/emit_object_assembly.py`](examples/emit_object_assembly.py) - 从 module 输出宿主机目标代码和汇编
- [`examples/jit_add.py`](examples/jit_add.py) - JIT 编译 IR,通过 ctypes 调用它,并注册一个 Python callback
- [`examples/instruction_metadata.py`](examples/instruction_metadata.py) - 将自定义元数据附加到指令并打印 IR
- [`examples/named_metadata.py`](examples/named_metadata.py) - 创建 module 命名元数据并打印 IR
- [`examples/metadata_debug_info.py`](examples/metadata_debug_info.py) - 附加元数据,创建调试信息,并使用调试位置作用域
- [`examples/transform_replace_add.py`](examples/transform_replace_add.py) - 使用 operands、RAUW 和指令删除的简单 IR 转换
- [`examples/bc-stats.py`](examples/bc-stats.py) - 从 LLVM IR 打印各函数的指令直方图
- [`examples/bc-graphviz.py`](examples/bc-graphviz.py) - 从 LLVM IR 生成 GraphViz 风格的控制流图
- [`examples/bc-profile.py`](examples/bc-profile.py) - 使用分析钩子检测 LLVM IR
## 当前功能
- 针对上下文、modules、类型、值、函数、基本块、builders、元数据、调试信息、目标文件、targets、目标机器和 pass builder 选项的 Python 风格包装器
- IR 构建、遍历和转换辅助工具:常量、全局变量、PHI/控制流/内存/cast/cmp 指令、operands、前驱节点、RAUW、分割基本块、移动/克隆/擦除指令
- IR 和 bitcode 解析/写入、惰性 bitcode 模块、module 克隆/链接、诊断、属性、COMDATs、调用约定、链接/可见性/存储控制
- 元数据/调试信息 API,包括命名元数据视图、module 标志视图、指令/全局元数据映射、DIBuilder 方法以及调试位置作用域
- 目标查找、数据布局、`Module.emit_object()`、`Module.emit_assembly()`、目标机器输出,以及通过 `Module.optimize()`、`Function.optimize()` 和 `Module.run_passes()` 执行的 PassBuilder 流水线
- 使用 `Builder.intrinsic(...)` 进行的通用内联函数调用,以及通过 LLVM-C ORC LLJIT API 进行的进程内 JIT 执行
- 生命周期/有效性保护机制,可将许多释放后使用、对象已释放、空引用和类型错误转化为 Python 异常,而不是导致程序直接崩溃
- 为 IDE 和类型检查器自动生成的带类型 `.pyi` 存根
- Golden-master 测试、Python 回归脚本、示例以及内置的 `llvm-c-test` lit 测试,包括针对已安装 Wheel 包的 CI 覆盖率测试
## 文档
类型存根是自动生成的,可提供 IDE 智能提示。构建完成后,可在以下位置找到它们:
```
.venv/lib/python3.*/site-packages/llvm/__init__.pyi
```
有关开发文档,请参阅 `devdocs/README.md`。
## 已知限制
- API 尚未稳定;方法名和所有权规则可能仍会更改。
- 作用域遵循 LLVM-C API,而不是完整的 LLVM C++ API。JIT 支持被有意限制在通过 LLVM-C ORC LLJIT 可以清晰暴露的范围内。
- 目前文档仅包含 README、示例、`devdocs/` 和生成的 `.pyi` 存根;尚无托管的 API 参考文档。
- 预构建的 Wheel 包目前针对 Linux x86_64/aarch64、macOS arm64/x86_64 和 Windows x86_64 上的 CPython 3.12+ 稳定 ABI。其他平台需要通过源码构建。
## 开发
### 设置
源码/开发构建需要 CMake 能找到 LLVM 21.1.6。用于打包的 LLVM 归档文件发布在 [LLVMParty/llvm-builds v21.1.6](https://github.com/LLVMParty/llvm-builds/releases/tag/v21.1.6)。
在检出代码后,下载对应的 LLVM 归档文件,然后运行 `uv sync`:
```
# 选择与您的平台匹配的压缩包:
# llvm-21.1.6-linux-x86_64.zip
# llvm-21.1.6-linux-aarch64.zip
# llvm-21.1.6-macos-arm64.zip
# llvm-21.1.6-macos-x86_64.zip
# llvm-21.1.6-windows-x86_64.zip
python tools/ci/install_llvm.py \
--version 21.1.6 \
--archive llvm-21.1.6-linux-x86_64.zip \
--dest .llvm \
--prefix-file .llvm-prefix
uv sync --verbose
```
如果您使用的是自己安装的 LLVM,请将 `LLVM_ROOT` 设置为其安装前缀:
```
export LLVM_ROOT=/path/to/llvm
uv sync --verbose
```
如果 `.llvm-prefix` 已经指向另一个已安装的 LLVM,请先将其删除;CMake 可能会复用之前配置时的路径。
对于离线构建:
```
uv sync --offline --no-build-isolation --verbose
```
关于 Windows 的 C++/LSP 设置,请参阅下方的 Windows 开发部分。
### 测试
```
# 主要 golden-master suite:
# - 从 build/ 运行 C++ 测试可执行文件
# - 运行配对的 Python 脚本
# - 将 Python 输出与存储的 C++ 行为进行比较
uv run run_tests.py
# tests/regressions/ 中的 Python-only 回归脚本
uv run run_tests.py --regressions
# 针对 C 测试二进制文件的 Vendored llvm-c-test lit suite
# 在运行 lit 之前重建 vendored C 二进制文件。
uv run run_llvm_c_tests.py
uv run run_llvm_c_tests.py -v
# 针对 Python 实现的 Vendored llvm-c-test lit suite
uv run run_llvm_c_tests.py --use-python
# 在开发期间直接运行 Python llvm-c-test port
uv run python -m llvm_c_test --targets-list
# Type checking(不是 test suite,但通常在 CI/开发中运行)
uvx ty check
```
如果您想尽可能实现“运行此代码库中的所有内容”,请使用:
```
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 测试旨在作为独立脚本执行
(例如 `uv run tests/test_module.py` 或 `uv run tests/regressions/test_const_bytes.py`)。
它们通常也与 pytest 兼容,
但直接脚本执行是 `run_tests.py` 使用的以及用于一次性调试的
历史/默认风格。
`pytest` 仍然可用于定位特定的回归文件或子集,
但它本身并不是我们完整的顶级测试入口。
### 覆盖率
```
# 带 coverage 运行
uv run coverage run run_llvm_c_tests.py --use-python
uv run coverage combine
uv run coverage report --include="llvm_c_test/*"
```
### Windows 源码构建和 C++ IntelliSense
在 Windows 上使用 Wheel 包是最简单的途径。如果您需要进行源码/开发构建,请安装带有 C++ 工作负载的 Visual Studio 或 Visual Studio Build Tools,然后获取 LLVM 并运行 `uv sync`:
```
py -3.12 tools\ci\install_llvm.py `
--version 21.1.6 `
--archive llvm-21.1.6-windows-x86_64.zip `
--dest .llvm `
--prefix-file .llvm-prefix
uv sync --verbose
```
该命令会从 [LLVMParty/llvm-builds v21.1.6](https://github.com/LLVMParty/llvm-builds/releases/tag/v21.1.6) 下载,安装到 `.llvm` 目录,并为 CMake 写入 `.llvm-prefix` 文件。
如果您使用的是自己安装的 LLVM:
```
$env:LLVM_ROOT = "C:\path\to\llvm"
uv sync --verbose
```
对于使用 LSP 进行 C++ 本地开发,请在代码库根目录下创建一个本地的 `CMakeUserPresets.json`。CMake 会从上面创建的 `.llvm-prefix` 获取 LLVM,或者在首次配置时从 `LLVM_ROOT` 获取。下方的编译器路径假设使用的是常规的 LLVM 安装位置;如果您的 `clang-cl.exe` 在其他位置,请进行调整:
```
{
"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"
}
}
],
"buildPresets": [
{
"name": "clang-cl",
"configurePreset": "clang-cl"
}
]
}
```
然后使用以下命令进行配置/构建:
```
cmake --preset clang-cl
cmake --build --preset clang-cl
```
如果您移动或替换了已安装的 LLVM,请删除任何先前配置生成的 `.llvm-prefix` 并重新生成。
## 许可证
本项目基于 MIT 许可证授权。详情请参阅 [LICENSE](LICENSE)。
LLVM 基于 Apache License v2.0 with LLVM Exceptions 授权。
标签:Bash脚本, LLVM, nanobind, Python绑定, 代码生成, 渗透测试工具, 编译器基础设施, 逆向工具