32个版本

0.11.1 2024年5月18日
0.11.0 2024年2月24日
0.10.0 2024年1月26日
0.9.0 2023年5月31日
0.2.1 2019年7月25日

63编程语言 中排名

Download history • Rust 包仓库 21/week @ 2024-05-02 • Rust 包仓库 35/week @ 2024-05-09 • Rust 包仓库 231/week @ 2024-05-16 • Rust 包仓库 40/week @ 2024-05-23 • Rust 包仓库 29/week @ 2024-05-30 • Rust 包仓库 31/week @ 2024-06-06 • Rust 包仓库 37/week @ 2024-06-13 • Rust 包仓库 41/week @ 2024-06-20 • Rust 包仓库 5/week @ 2024-06-27 • Rust 包仓库 4/week @ 2024-07-04 • Rust 包仓库 9/week @ 2024-07-11 • Rust 包仓库 12/week @ 2024-07-18 • Rust 包仓库 16/week @ 2024-07-25 • Rust 包仓库 10/week @ 2024-08-01 • Rust 包仓库 17/week @ 2024-08-08 • Rust 包仓库 18/week @ 2024-08-15 • Rust 包仓库

63 每月下载量
用于 6 crates

MIT 许可证

555KB
9K SLoC

llvm-ir:自然Rust数据结构中的LLVM IR

crates.io License

llvm-ir力求提供一个Rust风格的LLVM IR表示。它基于这样的想法:一个LLVM Instruction不应该是不可见的数据类型,而是一个具有AddCallStore等变体的enum。同样,像BasicBlockFunctionModule这样的类型应该是尽可能包含信息的Rust结构体。

inkwell等其他安全的LLVM绑定不同,llvm-ir不依赖于持续调用LLVM API的FFI。它只在解析步骤中使用LLVM API,以拉取构建其丰富的LLVM IR表示所需的所有数据。一旦llvm-ir通过解析LLVM文件(使用出色的llvm-sys底层LLVM绑定)创建了一个Module数据结构,它就丢弃了LLVM FFI对象,不再进行任何FFI调用。这使得您可以完全使用安全的Rust来处理生成的LLVM IR。

llvm-ir 用于消费 LLVM IR,但不一定是生产 LLVM IR(尚未)。也就是说,它旨在用于程序分析和相关应用,这些应用需要读取和分析 LLVM IR。未来,也许 llvm-ir 能够将它的 Module 输出到 LLVM 文件中,甚至直接发送到 LLVM 库进行编译。如果您对此感兴趣,欢迎贡献力量!(或者在此期间,您可以查看 inkwell 以获取生成 LLVM IR 的不同安全接口。)但如果您正在寻找一个适用于纯 Rust 的、面向读取的 LLVM IR 的良好表示,那么这正是 llvm-ir 今天可以提供的。

入门

这个软件包位于 crates.io,因此您只需将其作为依赖项添加到您的 Cargo.toml 中,选择与您想要的 LLVM 版本相对应的功能

[dependencies]
llvm-ir = { version = "0.11.1", features = ["llvm-18"] }

目前,支持的 LLVM 版本包括 llvm-9llvm-10llvm-11llvm-12llvm-13llvm-14llvm-15llvm-16llvm-17llvm-18

然后,开始的最简单方法是解析一些现有的 LLVM IR 到这个软件包的数据结构中。为此,您需要 LLVM 位码(*.bc)或文本格式 IR(*.ll)文件。如果您目前有 C/C++ 源文件(例如,source.c),您可以使用 clang-c-emit-llvm 标志生成 *.bc 文件

clang -c -emit-llvm source.c -o source.bc

或者,要编译 Rust 源文件到 LLVM 位码,您可以使用 rustc--emit=llvm-bc 标志。

在任何情况下,一旦您有了位码文件,您就可以使用 llvm-irModule::from_bc_path 函数

use llvm_ir::Module;
let module = Module::from_bc_path("path/to/my/file.bc")?;

或者如果您有一个文本格式 IR 文件,您可以使用 Module::from_ir_path()

您可能还会对 llvm-ir-analysis 软件包感兴趣,它可以计算 llvm-ir 函数的控制流图、支配树等。

文档

llvm-ir 的文档可以在 docs.rs 上找到,或者当然您可以使用 cargo doc --open 生成本地文档。当适用时,文档包括到 LLVM 文档相关部分的链接。

请注意,一些数据结构取决于您选择的 LLVM 版本而略有不同。docs.rs 文档使用 llvm-10 功能生成;对于其他 LLVM 版本,您可以使用以下命令获取适当的文档:cargo doc --features=llvm-<x> --open 其中 <x> 是您使用的 LLVM 版本。

兼容性

llvm-ir 0.7.0 开始,LLVM 版本通过 Cargo 功能标志选择。这意味着单个crate版本可以用于任何支持的 LLVM 版本。目前,llvm-ir 支持 9 到 18 的 LLVM 版本,通过功能标志 llvm-9llvm-18 选择。

您应选择与您链接的 LLVM 库版本相对应的 LLVM 版本(即,系统上可用的版本)。较新的 LLVM 应该能够读取较旧 LLVM 产生的位代码,因此您应该能够使用此 crate 解析通过 crate 功能标志选择的 LLVM 版本更旧的位代码,即使是由版本低于 LLVM 9 的 LLVM 产生的位代码。然而,我们并没有对这一点进行广泛的测试。

llvm-ir 在稳定版 Rust 上运行。截至本文撰写时,它需要 Rust 1.65+。

开发和调试

对于开发和调试,除了 *.bc 文件外,您可能还需要 *.ll 文件。

对于 C/C++ 源文件,您可以通过向 clang 传递 -S -emit-llvm 来生成这些文件,而不是传递 -c -emit-llvm。例如:

clang -S -emit-llvm source.c -o source.ll

对于 Rust 源文件,您可以使用 rustc--emit=llvm-ir 标志。

此外,您可能还需要在生成位代码时向 clangclang++rustc 传递 -g 标志。这将生成带有调试信息的 LLVM 位代码,这将确保 InstructionTerminatorGlobalVariableFunction 具有有效的 DebugLoc。 (参见 HasDebugLoc 特性。)请注意,这些 DebugLoc 只在 LLVM 9 及以上版本中可用;LLVM 的旧版本在该 C API 接口中有一个会导致段错误的 bug。

限制

llvm-ir 的数据结构中尚未表示一些 LLVM IR 的功能。

最值得注意的是,llvm-ir可以恢复调试位置元数据(用于映射回源位置),但并未尝试恢复其他任何调试元数据。包含元数据的LLVM文件仍然可以无问题地解析,但生成的Module结构将不包含任何元数据,除了调试位置。

由于LLVM C API和Rust llvm-sys crate中缺少它们的getter,因此llvm-ir数据结构缺少一些功能。这些包括但不限于

  • 各种浮点操作上的“快速数学标志”
  • 内联汇编函数的内容
  • 关于变长LandingPad指令的子句信息
  • 关于BlockAddress常量表达式的操作数信息
  • 关于TargetExtType类型的信息
  • 与函数关联的“前缀数据”
  • 大于64位(且不适合64位)的常量整数值(请参阅#5
  • CallBr终止符可达的“其他标签”(这是在LLVM 9中引入的)
  • (LLVM 16及以下版本——在LLVM 17及以后的版本中修复)AddSubMulShl上的nswnuw标志,以及类似的UDivSDivLShrAShr上的exact标志。C API具有创建指定这些标志值的新指令的功能,但不能查询现有指令上的这些标志值。
  • (LLVM 9及以下版本——在LLVM 10及以后的版本中修复)AtomicRMW指令的指令码,即XchgAddMaxMin等。

关于这方面的更多讨论请参阅LLVM错误编号42692。任何有助于填补C API中这些空缺的贡献都将受到高度赞赏!

致谢

llvm-ir最初受到了llvm-hs-pure Haskell包的启发。大多数原始版本中的数据结构基本上是将llvm-hs-pure中的数据结构从Haskell翻译成Rust的结果(进行了一些调整)。

0.7.0版本变更日志

llvm-ir 0.7.0与之前的版本相比有几个相当重大的变化,如下所述。

  • 现在通过Cargo功能选择LLVM版本。您必须选择以下功能之一:llvm-8llvm-9llvm-10。以前,我们有一个针对LLVM 10的0.6.x分支,针对LLVM 9的0.5.x分支,并且没有正式支持LLVM 8。现在,单个版本支持LLVM 8、9和10。
    • (注意:此crate的版本号超过0.7.0的版本已添加对后续LLVM版本的支持。例如,0.7.3及以后的版本也支持LLVM 11;0.7.5及以后的版本也支持LLVM 12。crate版本0.11.0删除了对LLVM 8的支持。)
  • FunctionAttributeParameterAttribute 现在是带有描述性变体的正确枚举,如 NoInlineStackProtect 等。之前,属性是难以解释的不透明数字代码。
  • 对运行时性能的改进以及特别是内存消耗的改进,尤其是在解析大型 LLVM 模块时。这涉及到对公共接口的许多破坏性更改。
    • Type 的大多数用户现在拥有一个 TypeRef,而不是直接拥有一个 Type。这包括 Operand::LocalOperandGlobalVariable、许多 Instruction 的变体、许多 Constant 的变体以及一些 Type 本身的变体等。请参阅 TypeRef 的文档。
    • 类似地,Constant 的大多数用户现在拥有一个 ConstantRef,而不是直接拥有一个 Constant。请参阅 ConstantRef 的文档。
    • 要获取 Typed 对象的类型,现在提供的 .get_type() 方法需要额外的参数;大多数用户可能更愿意使用 module.type_of()(或 module.types.type_of())。
    • Type::NamedStructType 不再携带对内部类型的弱引用;相反,您可以使用 module.types.named_struct_def() 来通过名称查找以获取模块中任何命名结构类型的定义。
  • 所需的 Rust 版本从 1.36+ 增加到 1.39+。
    • (注意:此存储库版本 0.7.0 之后的版本进一步提高了此要求。有关当前所需的 Rust 版本,请参阅上面的“兼容性”。)

依赖关系