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

jay*_*lps 12 c compiler-construction llvm clang bitcode

上下文

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

问题

我遇到的问题是clang似乎没有保留任何任何函数定义都没有使用的类型声明.铛具有其听起来像它应该解决这个问题,但不幸的是,不是和谷歌搜索表明它名不副实,因为它不仅影响未使用的定义,而不是声明.-femit-all-decls

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

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


例如,给出这个输入:

runtime.h

struct Foo {
  int a;
  int b;
};

struct Foo * something_with_foo(struct Foo *foo);
Run Code Online (Sandbox Code Playgroud)

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

runtime.ll

; ...etc...

%struct.Foo = type { i32, i32 }

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

; ...etc...
Run Code Online (Sandbox Code Playgroud)

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


有人问过这些年,但是对于这种大小和类型复杂性的运行时,提出的解决方案相当hacky并且相当不切实际:Clang - 将C头编译为LLVM IR/bitcode

Ban*_*nex 6

Clang 的预编译头实现似乎没有输出 LLVM IR,而是只输出 AST(抽象语法树),这样头就不需要再次解析:

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

底层的二进制格式可能是相同的,但听起来内容不同,LLVM 的位码格式在这种情况下只是一个容器。这从网站上的帮助页面看不是很清楚,所以我只是推测。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;
Run Code Online (Sandbox Code Playgroud)

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


Aja*_*iya 6

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

您可以在clang repo中查看这些行

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

这里的简单修复方法是注释最后两行或仅添加&& false到第二个条件。

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

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

为了使其更清晰,您还可以添加命令行标志--emit-all-declarations并在继续之前在此处进行检查。