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绑定, 代码生成, 渗透测试工具, 编译器基础设施, 逆向工具