CMakeList文件从C源文件生成LLVM bitcode文件

问题描述 投票:2回答:2

我正在尝试使用CMake从C源文件(hello.c)生成LLVM字节码文件。以下是我的CMakeLists文件。

###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-emit-llvm")

project (hello)
add_executable(hello hello.c)
  1. 我是CMake的新手,不确定这是否正确。我找不到任何规则来生成生成的MakeFile中的* .bc。请在这里纠正我。我也试过“-save-temps”
  2. 考虑到单个.c文件。如果您可以给我一些关于为完整的C项目生成相同内容的提示,那将非常有用。
linux makefile cmake llvm llvm-clang
2个回答
0
投票

我认为你最终想要的是能够用CMake和clang构建一个C程序项目,其中源文件被编译为LLVM bitcode,可执行文件从bitcode文件链接。

使用CMake,要求clang链接bitcode文件意味着要求它在LTO mode中与-flto链接选项链接。

你可以使用-flto编译选项或使用-emit-llvm选项编译成LLVM bitcode。

这里是一个Hello World项目,包括两个源文件和一个头文件:

$ ls -R
.:
CMakeLists.txt  hello.c  hello.h  main.c

这里是:

的CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)
project (hello)
set(CMAKE_C_COMPILER clang)
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello main.c hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)

它同样适用于:

#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)

为CMake制作一个构建目录并去那里:

$ mkdir build
$ cd build

生成构建系统:

$ cmake ..

建立:

$ make
Scanning dependencies of target hello
[ 33%] Building C object CMakeFiles/hello.dir/main.c.o
[ 66%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello

您不会在Makefile中找到任何*.bc目标,也不会生成任何*.bc文件:

$ egrep -r '.*\.bc'; echo Done
Done
$ find -name '*.bc'; echo Done
Done

因为编译选项-flto-emit-llvm会产生一个输出文件:

CMakeFiles/hello.dir/main.c.o
CMakeFiles/hello.dir/hello.c.o

遵循通常的CMake命名约定但实际上不是目标文件而是LLVM bitcode文件,如您所见:

$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o:  LLVM IR bitcode

该程序通常做的事情:

$ ./hello 
Hello World!

后来

当我尝试“make hello.o”时,它应该生成对象文件吗? cmd执行成功但无法找到生成的目标文件。我做得对吗?

你是以一种正确的方式做到这一点,虽然不是唯一正确的方式,但你的期望是错误的。再看一下:

$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o:  LLVM IR bitcode

你可以看到由CMake生成的makefile从.ohello.c制作的main.c文件不是hello.omain.o,而是hello.c.omain.c.o。 CMake更喜欢编译的文件名以保留源文件的扩展名,并附加.o。这是一种相当普遍的做法。因此,如果你想使用makefile来编译hello.c,最明显的正确方法是make hello.c.o

让我们看看究竟发生了什么。在我的CMake构建目录中:

$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
make[1]: 'CMakeFiles/hello.dir/hello.c.o' is up to date.
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

没有什么可做的,因为我的hello.c.o是最新的。所以我会删除它并重复:

$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang   -flto -o CMakeFiles/hello.dir/hello.c.o   -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

现在它已被重新编译。

然而,因为许多人 - 比如你 - 期望从hello.o汇编hello.c,CMake有助于将hello.o定义为依赖于.PHONY targethello.c.o

$ egrep  -A3 'hello.o.*:.*hello.c.o' Makefile 
hello.o: hello.c.o

.PHONY : hello.o

所以事实上我可以这样做:

$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang   -flto -o CMakeFiles/hello.dir/hello.c.o   -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

make hello.o是另一种制作hello.c.o的方式


0
投票

问题是使用-emit-llvm标志不会产生最终的二进制文件,并且一旦CMake执行该标志就会停止CMake执行的配置测试。

除了已经有关使用LTO基础设施的文章之外,您还有3个(或2个半)其他选择。

一种是使用Whole-Program LLVM并使用提供的命令来提取相关的bitcode部分。

另一种是在CMake二进制目标上设置自定义目标(请参阅add_custom_targetadd_custom_command)的手动方式,这些目标将在更改时触发,并将重现所需的结果,就像每次在命令行上手动执行一样。

现在,在最后一点上,我有类似的需求,所以我创建了一个提供该功能的CMake项目(llvm-ir-cmake-utils),但允许您根据需要将这些自定义目标连接到现有的目标,并且无需重写所有内容每次刮擦。

回购中有一些例子,但简而言之,它允许您在已有的CMake目标上附加自定义目标,例如

[...]
add_executable(qux ${SOURCES})

[...]
# this will create a bitcode generating target 
# and allow it to depend on the initial target in order to detect source code changes
llvmir_attach_bc_target(qux_bc qux)
add_dependencies(qux_bc qux)
[...]
© www.soinside.com 2019 - 2024. All rights reserved.