c/c++语言开发共享Clang – 将C头编译为LLVM IR / bitcode

假设我有以下简单的C头文件:

// foo1.h typedef int foo; typedef struct { foo a; char const* b; } bar; bar baz(foo*, bar*, ...); 

我的目标是获取此文件,并生成一个如下所示的LLVM模块

 %struct.bar = type { i32, i8* } declare { i32, i8* } @baz(i32*, %struct.bar*, ...) 

换句话说,将带有声明的C .h文件转换为等效的LLVM IR,包括类型解析,宏扩展等。

通过Clang传递它来生成LLVM IR会产生一个空模块(因为实际上没有使用任何定义):

 $ clang -cc1 -S -emit-llvm foo1.h -o - ; ModuleID = 'foo1.h' target datalayout = "em:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-darwin13.3.0" !llvm.ident = !{!0} !0 = metadata !{metadata !"clang version 3.5 (trunk 200156) (llvm/trunk 200155)"} 

我的第一直觉是转向谷歌,我遇到了两个相关的问题: 一个来自邮件列表 , 另一个来自StackOverflow 。 两者都建议使用-femit-all-decls标志,所以我尝试了:

 $ clang -cc1 -femit-all-decls -S -emit-llvm foo1.h -o - ; ModuleID = 'foo1.h' target datalayout = "em:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-darwin13.3.0" !llvm.ident = !{!0} !0 = metadata !{metadata !"clang version 3.5 (trunk 200156) (llvm/trunk 200155)"} 

结果相同。

我也尝试过禁用优化(使用-O0-disable-llvm-optzns ),但这对输出没有影响。 使用以下变体确实产生了所需的IR:

 // foo2.h typedef int foo; typedef struct { foo a; char const* b; } bar; bar baz(foo*, bar*, ...); void doThings() { foo a = 0; bar myBar; baz(&a, &myBar); } 

然后运行:

 $ clang -cc1 -S -emit-llvm foo2.h -o - ; ModuleID = 'foo2.h' target datalayout = "em:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-darwin13.3.0" %struct.bar = type { i32, i8* } ; Function Attrs: nounwind define void @doThings() #0 { entry: %a = alloca i32, align 4 %myBar = alloca %struct.bar, align 8 %coerce = alloca %struct.bar, align 8 store i32 0, i32* %a, align 4 %call = call { i32, i8* } (i32*, %struct.bar*, ...)* @baz(i32* %a, %struct.bar* %myBar) %0 = bitcast %struct.bar* %coerce to { i32, i8* }* %1 = getelementptr { i32, i8* }* %0, i32 0, i32 0 %2 = extractvalue { i32, i8* } %call, 0 store i32 %2, i32* %1, align 1 %3 = getelementptr { i32, i8* }* %0, i32 0, i32 1 %4 = extractvalue { i32, i8* } %call, 1 store i8* %4, i8** %3, align 1 ret void } declare { i32, i8* } @baz(i32*, %struct.bar*, ...) #1 attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.ident = !{!0} !0 = metadata !{metadata !"clang version 3.5 (trunk 200156) (llvm/trunk 200155)"} 

除了占位符doThings ,这正是我想要的输出看起来像! 问题是这需要1.)使用标题的修改版本,以及2.)事先知道事物的类型。 这导致我……

为什么?

基本上,我正在使用LLVM构建一种语言实现来生成代码。 该实现应该通过仅指定C头文件和关联的lib(无手动声明)来支持C interop,然后编译器将在链接时使用它以确保函数调用与其签名匹配。 因此,我已将问题缩小到2种可能的解决方案:

TL; DR

我需要获取一个C头文件(例如上面的foo1.h ),并且在不更改它的情况下,使用Clang,OR生成上述预期的LLVM IR,找到从C头文件获取函数签名的另一种方法 (最好使用libclang或者构建C解析器)

    也许是不太优雅的解决方案,但仍然坚持使用doThings函数强制编译器发出IR,因为使用了以下定义:

    您使用此方法确定的两个问题是它需要修改标头,并且需要更深入地了解所涉及的类型,以便生成“使用”以放入函数。 这些都可以相对简单地克服:

    这极大地简化了解析步骤,因为您只需要识别struct / union或函数声明的上下文,而实际上不需要对周围的信息做很多事情。


    一个简单而又虚伪的起点(我可能会使用,因为我的标准低:D)可能是:

    它不会抓住所有可能性; 但是通过一些调整和扩展,它实际上可能会处理大量实际标题代码。 您可以在稍后阶段使用专用的简化解析器(仅构建为仅查看所需上下文的模式)来替换它。

    需要了解更多c/c++开发分享Clang – 将C头编译为LLVM IR / bitcode,也可以关注C/ C++技术分享栏目—计算机技术网(www.ctvol.com)!

      以上就是c/c++开发分享Clang – 将C头编译为LLVM IR / bitcode相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/980401.html

      (0)
      上一篇 2021年12月13日
      下一篇 2021年12月13日

      精彩推荐