如何让Clang编译为LLVM IR

197

我希望使用 clang 将我的 C/C++ 代码编译为 LLVM 比特码,而不是二进制可执行文件。我该如何实现?

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

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

5个回答

261

给定一个 C/C++ 文件 foo.c

> clang -S -emit-llvm foo.c

生成一个LLVM IR文件 foo.ll

可以通过直接向编译器前端传递 -emit-llvm 选项,而不是通过 -cc1 驱动程序来传递。

> clang -cc1 foo.c -emit-llvm

使用 -cc1 可以生成带有IR的foo.ll文件。同时,-cc1 还提供了一些很酷的选项,如-ast-print。想了解更多细节,请查看-cc1 --help

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

> llc foo.ll

使用llc工具生成汇编文件 foo.s,默认为您运行它的机器体系结构。 llc是LLVM工具之一 - 这里是其文档


12
这里的-S是什么作用? - meawoppl
22
"S"在gcc中的意思是生成文本汇编代码而非已汇编的二进制文件。 - Eli Bendersky
啊哈。我在文档中找不到任何关于它的信息。可以安全地假设,clang 中的许多标志镜像了 gcc 的标志结构吗? - meawoppl
1
@EliBendersky 你知道如何将多个.c和.h文件编译成一个可读的IR,以便我可以使用“lli theIrFile”运行IR吗?谢谢。 - cache
4
请问需要翻译成中文吗?如果是的话,我可以这么翻译:将每个文件编译成其自己的IR文件,然后使用LLVM链接器进行组合。 - Eli Bendersky
显示剩余3条评论

28

如果您有多个源文件,您可能确实想要使用链接时优化来输出整个程序的一个位码文件。其他提供的答案将导致您最终针对每个源文件都有一个位码文件。

相反,您需要使用链接时优化编译。

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

这会给你一个已编译程序和对应的位代码 (program.bc)。你可以随意修改 program.bc,并随时重新编译修改后的程序:

clang program.bc -o program

请注意,在这一步中,您需要包括任何必要的链接器标志(用于外部库等)。

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

-B/home/jeremy/fakebin

到任何上面的链接步骤。


显然 also-emit-llvm 已被移除。 - Heath Mitchell

25

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

  1. 生成所有.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
    

    1
    你的解决方案非常独特:你使用了“-S”而不是仅保留二进制输出。有没有使用“-S”和不使用“-S”之间的区别? - Peter Teoh
    1
    @PeterTeoh 我在第2步中使用了“-S”选项,指定我想要以LLVM IR格式生成输出。基本上,将所有*.ll文件放入一个单独的文件中。我这样做是为了检查优化是否真正改变了代码,即“single.ll”和“optimised.ll”现在应该在代码方面看起来不同,您还可以显示报告以查看是否有任何差异。 - Kiko Fernandez
    1
    -basicaaa是错误的标志,应该使用-basicaa代替。 - anton_rh

    24

    使用

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

    9
    我建议保留扩展名的原有含义。也就是说,".o"应该指代二进制目标文件,".s"指代汇编文件,而其他(按约定使用“.ll”)则指代LLVM IR文件。否则很容易混淆。Clang/LLVM现在没有自己的链接器来处理二进制目标文件(尽管正在开发一个)。LLVM链接器“llvm-ld”只是将多个IR文件连接成一个。 - Eli Bendersky
    2
    @EliBendersky:对于文件扩展名,您是正确的-如果使用.bc,clang前端实际上会做正确的事情;另外,请记住,llvm-ld可以作为系统工具链的前端,即我之前使用llvm-ld -native的答案应该按预期工作.... - Christoph
    1
    @rickfoosusa:对我来说没问题 - foo.bc 是一个LLVM位码文件。 - Christoph
    1
    针对我的系统有效:clang -emit-llvm -o test.bc -c test.c && file test.bc: test.bc: LLVM IR bitcode. - ntc2

    14

    你有没有阅读过clang文档?如果是,那么你可能想要使用-emit-llvm参数。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接