首页 文章

如何使用clang -emit-llvm编译并保留“未使用的”C声明

提问于
浏览
12

上下文

我使用LLVM作为我的后端,因此codegen需要所有这些运行时类型(函数,结构等)的类型,而不是使用LLVM API手动定义所有这些类型或手写LLVM IR我想编写C中的头文件并编译为编译器可以使用 LLVMParseBitcodeInContext2 引入的bitcode .

问题

问题我似乎保留了任何任何函数定义都没有使用的类型声明 . Clang有 -femit-all-decls ,听起来像是's supposed to solve it, but it unfortunately isn' t和谷歌搜索表明它的名字错误,因为它只影响未使用的定义,而不是声明 .

然后我想也许如果我只将头文件编译成 .gch 文件,我可以用 LLVMParseBitcodeInContext2 以同样的方式将它们拉进去(因为文档说他们使用"the same" bitcode格式“,但是这样做错误 error: Invalid bitcode signature 因此必须有所不同 . 也许差别小到可以解决?

任何可以为复杂运行时自动化的建议或相对简单的解决方法?如果有人对接近这个一般用例有一个完全替代的建议,我也会感兴趣,记住我不想在运行时函数体中静态链接我生成的每个单个目标文件,只是类型 . 我想这也是其他编译器所需要的,所以如果我接近这个错误,我不会感到惊讶 .


例如鉴于此输入:

runtime.h

struct Foo {
  int a;
  int b;
};

struct Foo * something_with_foo(struct Foo *foo);

我需要一个具有此等效IR的bitcode文件

runtime.ll

; ...etc...

%struct.Foo = type { i32, i32 }

declare %struct.Foo* @something_with_foo(%struct.Foo*)

; ...etc...

我可以手动编写它,但这将是重复的,因为我还需要为其他互操作创建C头,并且理想的是不必手动保持它们同步 . 运行时相当大 . 我想我也可以做相反的事情:在LLVM IR中编写声明并生成C头 .


今年有人问过这个问题,但是对于这种大小和类型复杂的运行时来说,所提出的解决方案相当苛刻且相当不切实际:Clang - Compiling a C header to LLVM IR/bitcode

2 回答

  • 4

    所以, clang 实际上并没有过滤掉未使用的声明 . 它推迟发出前向声明,直到第一次使用 . 每当使用一个函数时,它会检查它是否已经被发出,如果没有,它会发出函数声明 .

    您可以在clang repo中查看these lines .

    // Forward declarations are emitted lazily on first use.
    if (!FD->doesThisDeclarationHaveABody()) {
      if (!FD->doesDeclarationForceExternallyVisibleDefinition())
        return;
    

    这里的简单修复是注释最后两行或只是将 && false 添加到第二个条件 .

    // Forward declarations are emitted lazily on first use.
    if (!FD->doesThisDeclarationHaveABody()) {
      if (!FD->doesDeclarationForceExternallyVisibleDefinition() && false)
        return;
    

    这将导致 clang 在看到它时立即发出声明,这也可能会更改定义在 .ll (或 .bc )文件中出现的顺序 . 假设这不是问题 .

    为了使它更干净,您还可以添加命令行标志 --emit-all-declarations 并在继续之前检查此处 .

  • 2

    Clang的precompiled headers实现似乎不输出LLVM IR,而只输出AST(抽象语法树),因此不需要再次解析标头:

    AST文件本身包含Clang的抽象语法树和支持数据结构的序列化表示,使用与LLVM的bitcode文件格式相同的压缩比特流存储 .

    底层二进制格式可能相同,但听起来内容不同,LLVM的bitcode格式在这种情况下仅仅是一个容器 . 这在网站的帮助页面上并不是很清楚,所以我只是在猜测 . LLVM / Clang专家可以帮助澄清这一点 .

    不幸的是,似乎并没有一种优雅的方式 . 我建议最小化实现所需的工作量是构建一个最小的C / C源文件,它以某种方式使用您要编译为LLVM IR的所有声明 . 例如,您只需要声明一个指向结构的指针,以确保它不会被优化掉,并且您可能只为函数提供一个空的定义以保持其签名 .

    获得最小的源文件后,使用 clang -O0 -c -emit-llvm -o precompiled.ll 进行编译,以获得具有LLVM IR格式的所有定义的模块 .

    您发布的代码段中的示例:

    struct Foo {
      int a;
      int b;
    };
    
    // Fake function definition.
    struct Foo *  something_with_foo(struct Foo *foo)
    {
        return NULL;
    }
    
    // A global variable.
    struct Foo* x;
    

    显示定义的输出:https://godbolt.org/g/2F89BH

相关问题