#llvm #pass #safe-wrapper #wrapper #safe

llvm-plugin

在 Rust 中轻松实现树外 LLVM 插件的包装器

12 个版本 (6 个重大更新)

0.6.0 2024年8月5日
0.5.0 2024年2月10日
0.4.1 2023年5月3日
0.3.0 2023年1月21日
0.0.0-保留2022年9月2日

#41 in FFI

Download history 1/week @ 2024-05-16 1/week @ 2024-05-23 105/week @ 2024-08-01 22/week @ 2024-08-08

每月下载 127 次

Apache-2.0

90KB
1.5K SLoC

Rust 1K SLoC // 0.0% comments C++ 323 SLoC // 0.0% comments

llvm-plugin-rs

version doc linux windows macos

此 crate 通过利用 Inkwell 提供的强类型接口,使您能够安全地实现针对新 LLVM 插件管理器的 passes。

如果您以前从未开发过 LLVM passes,您可以查看可用的 示例。它们(希望)会给您一个更好的了解如何使用此 crate。

如果您想深入了解围绕新 LLVM 插件管理器的许多概念,您应该阅读官方 LLVM 文档

用法

当在您的 Cargo.toml 中导入此 crate 时,您需要指定要使用的 LLVM 版本以及相应的功能标志

[dependencies]
llvm-plugin = { version = "0.6", features = ["llvm18-0"] }

支持版本:LLVM 10-18,对应于 cargo 功能标志 llvm*-0,其中 * 对应于 LLVM 的主要版本。

入门

LLVM 插件只是一个由 LLVM 工具(例如 optlld)加载时提供 PassBuilder 的 dylib。因此,您必须在您的 Cargo.toml 中添加以下行

[lib]
crate-type = ["cdylib"]

PassBuilder 允许在 LLVM 工具执行特定操作时注册回调。

例如,opt 命令的 --passes 参数允许指定在给定的 IR 模块上运行的定制 pass 管道。因此,插件可以注册一个回调来解析给定管道的一个元素(例如,pass 名称),以便通过 opt 插入一个自定义的 pass。

以下代码说明了这个想法

use llvm_plugin::inkwell::module::Module;
use llvm_plugin::{
    LlvmModulePass, ModuleAnalysisManager, PassBuilder, PipelineParsing, PreservedAnalyses,
};

// A name and version is required.
#[llvm_plugin::plugin(name = "plugin_name", version = "0.1")]
fn plugin_registrar(builder: &mut PassBuilder) {
    // Add a callback to parse a name from the textual representation of
    // the pipeline to be run.
    builder.add_module_pipeline_parsing_callback(|name, manager| {
        if name == "custom-pass" {
            // the input pipeline contains the name "custom-pass",
            // so we add our custom pass to the pass manager
            manager.add_pass(CustomPass);

            // we notify the caller that we were able to parse
            // the given name
            PipelineParsing::Parsed
        } else {
            // in any other cases, we notify the caller that our
            // callback wasn't able to parse the given name
            PipelineParsing::NotParsed
        }
    });
}

struct CustomPass;
impl LlvmModulePass for CustomPass {
    fn run_pass(
        &self,
        module: &mut Module,
        manager: &ModuleAnalysisManager
    ) -> PreservedAnalyses {
        // transform the IR
        todo!()
    }
}

现在,执行此命令将在某些输入 module.bc 上运行我们的自定义 pass

opt --load-pass-plugin=libplugin.so --passes=custom-pass module.bc -disable-output

但是,执行此命令不会(custom-pass2 无法被我们的插件解析)

opt --load-pass-plugin=libplugin.so --passes=custom-pass2 module.bc -disable-output

还有更多回调可用,有关更多详细信息,请参阅 文档

要了解如何顺序应用多个 pass 的更多信息,请参阅此 opt 指南

Linux & MacOS 要求

您的 LLVM 工具链应该动态链接 LLVM 库。幸运的是,在 apthomebrew 注册表中分发的工具链都是这种情况。

使用 apt 安装 LLVM-14
$ apt install llvm-14
使用 homebrew 安装 LLVM-14
$ brew install llvm@14

如果您不使用这些包管理器中的任何一个,您可以从此 LLVM 分支 下载兼容的 LLVM 工具链。在这种情况下,不要忘记使用您的 LLVM 工具链路径更新您的 PATH 环境变量,或者使用 LLVM_SYS_XXX_PREFIX 环境变量来定位您的工具链。

例如,如果您的 LLVM-14 工具链位于 ~/llvm,您应该设置以下任一选项

  • PATH=$PATH;$HOME/llvm/bin
  • LLVM_SYS_140_PREFIX=$HOME/llvm

Windows 要求

Windows 的官方 LLVM 工具链未启用插件支持。但是,可以在 此处 找到兼容的工具链。

不要忘记使用您的 LLVM 工具链路径更新您的 PATH 环境变量,或者使用 LLVM_SYS_XXX_PREFIX 环境变量来定位您的工具链。

例如,如果您的 LLVM-14 工具链位于 C:\llvm,您应该设置以下任一选项

  • PATH=$PATH;C:\llvm\bin
  • LLVM_SYS_140_PREFIX=C:\llvm

使用自定义 LLVM 插件编译 Rust/C++ 代码

LLVM 分支 解释了如何进行操作,并提供了一些将使该过程更简单的 LLVM 工具链。

缺少的功能

  • 对循环 pass 的支持(Inkwell 目前不提供安全的包装器)
  • 对 CGSCC pass 的支持(Inkwell 目前不提供安全的包装器)
  • 在完整的 manager 代理 API 上使用 FFI(仅实现了子集)
  • 在完整的分析无效化 API 上使用 FFI(仅实现了子集)
  • 在内置的 LLVM 分析上使用 FFI(例如,支配树)

欢迎贡献,请首先查看 贡献指南

依赖关系

~1.2–2.3MB
~43K SLoC