如何使 clang 编译为 llvm IR

Posted

技术标签:

【中文标题】如何使 clang 编译为 llvm IR【英文标题】:How to make clang compile to llvm IR 【发布时间】:2012-02-27 06:22:27 【问题描述】:

我希望 clang 将我的 C/C++ 代码编译为 LLVM 位码,而不是二进制可执行文件。我怎样才能做到这一点?

如果我有 LLVM 位码,如何进一步将其编译为二进制可执行文件?

我想在编译为二进制可执行文件之前将一些我自己的代码添加到 LLVM 位码。

【问题讨论】:

【参考方案1】:

给定一些 C/C++ 文件foo.c:

> clang -S -emit-llvm foo.c

生成foo.ll,这是一个 LLVM IR 文件。

-emit-llvm 选项也可以直接传递给编译器前端,而不是通过-cc1 的方式传递给驱动程序:

> clang -cc1 foo.c -emit-llvm

使用 IR 生成 foo.ll-cc1 添加了一些很酷的选项,例如 -ast-print。查看-cc1 --help了解更多详情。


要进一步编译 LLVM IR 以进行汇编,请使用 llc 工具:

> llc foo.ll

使用程序集生成foo.s(默认为您运行它的机器架构)。 llc 是 LLVM 工具之一 - here is its documentation。

【讨论】:

-S 在这里做什么? @meawoppl: -S 就像在 gcc 中表示发出文本汇编而不是汇编二进制 啊哈。我很难在文档中找到有关它的任何内容。假设 clang 镜像 gcc 标志结构中有许多标志是安全的吗? @EliBendersky 你知道如何将多个 .c 和 .h 文件编译成一个人类可读的 IR,以便我可以使用 'lli theIrFile' 运行 IR?谢谢 @cache:将每个编译成自己的 IR 文件,然后使用 LLVM 链接器进行组合【参考方案2】:

使用

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

【讨论】:

我建议保持扩展含义不变。 IOW,.o 应该引用二进制对象文件,.s 应该引用汇编文件,而其他东西(按照约定 .ll)应该引用 LLVM IR 文件。否则很容易混淆。 Clang/LLVM 现在没有自己的二进制对象链接器(尽管正在开发中)。 LLVM 链接器llvm-ld 只是将几个 IR 文件合并为一个 @EliBendersky:你在文件扩展名方面是正确的——如果使用.bc,clang 前端实际上是正确的;另外,请记住llvm-ld 可以充当系统工具链的前端,即我之前使用llvm-ld -native 的答案应该可以按预期工作...... @rickfoosusa: 为我工作 - foo.bc 是一个 LLVM 位码文件 为我工作:clang -emit-llvm -o test.bc -c test.c && file test.bc: test.bc: LLVM IR bitcode.【参考方案3】:

如果您有多个源文件,您可能实际上想要使用链接时间优化来为整个程序输出一个位码文件。给出的其他答案将导致您最终得到每个源文件的位码文件。

相反,您希望使用链接时间优化进行编译

clang -flto -c program1.c -o program1.o
clang -flto -c program2.c -o program2.o

对于最后的链接步骤,添加参数 -Wl,-plugin-opt=also-emit-llvm

clang -flto -Wl,-plugin-opt=also-emit-llvm program1.o program2.o -o program

这会给你both一个编译的程序和对应的位码(program.bc)。然后你可以用任何你喜欢的方式修改program.bc,并随时重新编译修改后的程序

clang program.bc -o program

但请注意,您需要在此步骤再次包含任何必要的链接器标志(用于外部库等)。

请注意,您需要使用黄金链接器才能使其正常工作。如果您想强制 clang 使用特定的链接器,请在计算机上某个名为“fakebin”的特殊目录中创建一个指向该链接器的符号链接,该链接器名为“ld”,并添加选项

-B/home/jeremy/fakebin

到上述任何链接步骤。

【讨论】:

【参考方案4】:

如果您有多个文件并且不想键入每个文件,我建议您按照以下简单步骤操作(我使用的是clang-3.8,但您可以使用任何其他版本):

    生成所有.ll 文件

    clang-3.8 -S -emit-llvm *.c
    

    将它们链接成一个

    llvm-link-3.8 -S -v -o single.ll *.ll
    

    (可选)优化你的代码(也许是一些别名分析)

    opt-3.8 -S -O3 -aa -basicaaa -tbaa -licm single.ll -o optimised.ll
    

    生成程序集(生成optimised.s 文件)

    llc-3.8 optimised.ll
    

    创建可执行文件(命名为a.out

    clang-3.8 optimised.s
    

【讨论】:

您的解决方案非常独特:您使用“-S”而不是将其保留为二进制输出。有“-S”和没有“-S”有什么区别吗? @PeterTeoh 我使用-S 选项(在步骤 2 中),我指定我想在 LLVM IR 中生成输出。基本上,将所有 *.ll 文件放入一个文件中。我这样做是为了检查优化是否真的改变了代码,即 single.lloptimised.ll 现在看起来应该不同(代码方面),您还可以显示报告以查看是否有任何差异。 -basicaaa 是错误标志,必须使用-basicaa【参考方案5】:

你读过clang documentation 吗?您可能正在寻找-emit-llvm

【讨论】:

以上是关于如何使 clang 编译为 llvm IR的主要内容,如果未能解决你的问题,请参考以下文章

如何使用LLVM将C语言编译为WASM

LLVM如何优化函数

Impala中 LLVM 的交叉编译、调用过程

OpenCL 内核的 LLVM IR 到 PTX 到二进制

LLVM 之 IR 篇:如何编写生成 LLVM IR 的工具

LLVM 之 IR 篇:如何使用 LLVM IR 优化器