当前位置: 首页 > 工具软件 > llvm-mctoll > 使用案例 >

Clang/LLVM使用

颛孙正卿
2023-12-01
下载和准备工作

先到这个网页点进到最新版的 download 连结:
http://llvm.org/releases/
然后抓 LLVM source codeClang source code 这两个档案。
接着把 LLVM source code 解开,
再把 Clang source code 解开丢到 llvm 的tools 目录下。
Clang source code 解开的目录名是 clang-2.7 之类的话把它改成 clang,
虽然我也没有去测试不改会怎样。

打算使用 clang++ 编译 C++ 程序的注意事项

编译和安装

为了不弄脏 source tree,
建议采用 out-of-source build 的方式编译。
先到 llvm 以外的地方造一个目录,
然后再下 cmake 来产生 Makefile。
configure 的下法我就不写了,
都 2010 年了哪还有傻子看见CMakeLists.txt 还会去跑 configure?
当然 CMakeLists.txt 没写好或乱写的状况下例外。

cmake 的下法这边以 tcsh 为例:

env CFLAGS=-fno-strict-aliasing CXXFLAGS=-fno-strict-aliasing

cmake -i path/to/llvm/source

或者把src解压后,在主目录下面如果任何project有一个CMakeLists.txt,可以直接在目录下建立一个build文件夹。

然后在build文件夹下运行, cmake ../  (../上一级目录,也可以是source解压后的绝对路径)

加上 -i 就会以 wizard mode 启动 cmake,
高兴的话也可以用 cmake-gui 跑。
我知道有人看到 -fno-strict-aliasing 会非常想笑,
但是不加的话光从 warning messages 来看是有一定机率编出坏掉的玩具
这个问题是出在 clang 的 code 上,
所以如果走 llvm-gcc 这条路的应该不会遇上。

出来的选项可以学这样填:

Would you like to see advanced options? [No]:<Enter>
Please wait while cmake processes CMakeLists.txt files....

Variable Name: CLANG_BUILD_EXAMPLES
Description: Build CLANG example programs.
Current Value: OFF
New Value (Enter to keep current value): <Enter>

Variable Name: CLANG_TEST_USE_VG
Description: Run Clang tests under Valgrind
Current Value: OFF
New Value (Enter to keep current value): <Enter>

Variable Name: CMAKE_BUILD_TYPE
Description: Choose the type of build, options are: None(CMAKE_CXX_FLAGS orCMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.
Current Value:
New Value (Enter to keep current value): RelWithDebInfo

Variable Name: CMAKE_INSTALL_PREFIX
Description: Install path prefix, prepended onto install directories.
Current Value: /usr/local
New Value (Enter to keep current value): 就是 make install 以后会装进去的路径

Variable Name: C_INCLUDE_DIRS
Description: Colon separated list of directories clang will search for headers.
Current Value:
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_BUILD_32_BITS
Description: Build 32 bits executables and libraries.
Current Value: OFF
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_BUILD_EXAMPLES
Description: Build LLVM example programs.
Current Value: OFF
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_BUILD_TOOLS
Description: Build LLVM tool programs.
Current Value: ON
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_ENABLE_ASSERTIONS
Description: Enable assertions
Current Value: ON
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_ENABLE_PEDANTIC
Description: Compile with pedantic enabled.
Current Value: ON
New Value (Enter to keep current value): OFF

Variable Name: LLVM_ENABLE_PIC
Description: Build Position-Independent Code
Current Value: ON
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_ENABLE_THREADS
Description: Use threads if available.
Current Value: ON
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_ENABLE_WARNINGS
Description: Enable compiler warnings.
Current Value: ON
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_ENABLE_WERROR
Description: Fail and stop if a warning is triggered.
Current Value: OFF
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_LIBDIR_SUFFIX
Description: Define suffix of library directory name (32/64)
Current Value:
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_TABLEGEN
Description: Native TableGen executable. Saves building one whencross-compiling.
Current Value: tblgen
New Value (Enter to keep current value): <Enter>

Variable Name: LLVM_TARGETS_TO_BUILD
Description: Semicolon-separated list of targets to build, or "all".
Current Value:Alpha;ARM;Blackfin;CBackend;CellSPU;CppBackend;Mips;MBlaze;MSIL;MSP430;PIC16;PowerPC;Sparc;SystemZ;X86;XCore
New Value (Enter to keep current value): ARM;CBackend;CppBackend;Mips;X86(反正就选高兴的用,不然干脆 all也行)

Variable Name: LLVM_TARGET_ARCH
Description: Set target to use for LLVM JIT or use "host" forautomatic detection.
Current Value: host
New Value (Enter to keep current value): <Enter>

Variable Name: NM_PATH
Description: Path to a program.
Current Value: /usr/bin/nm
New Value (Enter to keep current value): <Enter>

Please wait while cmake processes CMakeLists.txt files....

CMake complete, run make to build project.

然后直接用 GNU make 编译:

gmake -j9 install
N 可以是工作站的 CPU threads 总数 + 1。

无法使用 CMake 的状况

学习自己编译和安装的理由

因为我本身是做 compiler 的,
所以总有一天会需要去修改它们的 source code。
使用 OS 提供的套件系统来安装 LLVM,
是纯粹以只会使用它的角度来考虑。
若是以一名开发者的角度来考虑的话,
就需要学习如何手动编译及安装到自己的目录下。

基本用法

先随便造一个范例程序:

?Downloadtest.c

#include <stdio.h>

 

int main(int argc,char**argv)

{

  printf("hello world\n");

  return 0;

}

直接编译成可执行文件:

clang test.c -o test

这跟 GCC 没什么差别。

编译成 object file:

clang -c test.c

会得到 test.o,
这也跟 GCC 没什么差别。

输出 assembly code:

clang -S test.c

会得到 test.s,
还是跟 GCC 没有什么差别。

接下来就是 LLVM 特有的东西了。
-c 搭配 -emit-llvm 会输出 bitcodefile,
你可以把它想成是 target 在 llvm 这平台上的 object file。
-S 搭配 -emit-llvm 会输出所谓的 LLVMassembly code。

编译成 bitcode file:

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

如果你不加后面的 -o test.bc 参数,
目前我手边的版本会输出 test.o 这样的档名。
虽然说 .bc 这种 suffix 只是一个建议的命名惯例,
而且 llvm 的 command tools 也不会因为 suffix 是 .o 而搞混它,
但为了避免人类搞混我建议还是把它输出成带 .bc suffix 的檔名。
这个档案用系统的 file 指令测试的话,
应该会告诉你它是个 data 而不是 object file。

输出 LLVM assembly code:

clang -S -emit-llvm test.c -o test.ll

-o test.ll 的理由同上,
不加的话你会得到 test.s 这种档名而容易跟一般assembly file 混淆。
得到的档案内容会长这样:

?View Code TEXT

; ModuleID = 'test.c'

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

target triple = "x86_64-unknown-linux-gnu"

 

@.str = private constant [13 x i8] c"hello world\0A\00" ; <[13 x i8]*> [#uses=1]

 

define i32 @main(i32 %argc, i8** %argv) nounwind {

entry:

  %retval = alloca i32, align 4                   ; <i32*> [#uses=2]

  %argc.addr = alloca i32, align 4                ; <i32*> [#uses=1]

  %argv.addr = alloca i8**, align 8               ; <i8***> [#uses=1]

  store i32 0, i32* %retval

  store i32 %argc, i32* %argc.addr

  store i8** %argv, i8*** %argv.addr

  %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([13 x i8]* @.str, i32 0, i32 0)) ; <i32> [#uses=0]

  %0 = load i32* %retval                          ; <i32> [#uses=1]

  ret i32 %0

}

 

declare i32 @printf(i8*, ...)

好奇怎么读它的话可以参考这个网页:
http://llvm.org/docs/LangRef.html

以直译的方式 (含 JIT 功能) 直接执行 bitcode file:

lli test.bc

把bitcode file 反组译回 LLVM assembly code:

llvm-dis test.bc

这指令会自动输出 test.ll 这种档名,
所以不必像上面那样特地加参数指定。

组译 LLVM assembly code:

llvm-as test.ll

这会得到一个新的 test.bc。

多档编译与连结

先摆两个测试程序:

?Downloadtest1.c

void foo();

 

int main(int argc,char**argv)

{

  foo();

  return 0;

}

?Downloadtest2.c

#include <stdio.h>

 

void foo()

{

  printf("hello world\n");

}

编译:

clang -c -emit-llvm test1.c -o test1.bc
clang -c -emit-llvm test2.c -o test2.bc

连结:

llvm-link test1.bc test2.bc -o test.bc

执行:

lli test.bc

单独执行 test1.bc 和 test2.bc 会发生什么事?

函式库的制作与连结

直接沿用上面的例子。
把 test2.bc 封装成 libTest.a:

llvm-ar rucs libTest.a test2.bc

当然这个并不是普通的 archive file。

查看 symbol table:

llvm-nm libTest.a

连结:

llvm-ld test1.bc -o test -lTest

这样会得到两个档案。
一个是 test.bc,
你可以用 lli test.bc 去执行它。
另一个是叫 test 的 script 檔,
大概长这样:

?View Code BASH

#!/bin/sh

lli=${LLVMINTERP-lli}

exec $lli \

    test.bc ${1+"$@"}

其实也不过就是自动叫 lli 帮你直译而已。
如果你不希望它是一个 script 档而是真正的可执行文件,
加上 -native 参数就可以了。

Backend Compiler

如果说 LLVM assembly code 是 Open64 的 WHIRL 的话,
llc 的地位大致上相当于 Open64 的 be。

llc 的主要目的是把 bitcode 编译成特定平台的 assembly code,
而要输出哪个平台可以用 llc -version 查看:

Low Level Virtual Machine (http://llvm.org/):
llvm version 2.7svn
Optimized build with assertions.
Built Sep 10 2010 (02:45:40).
Host: x86_64-unknown-linux-gnu
Host CPU: core2

Registered Targets:
arm - ARM
c - C backend
cpp - C++ backend
mips - Mips
mipsel - Mipsel
thumb - Thumb
x86 - 32-bit X86: Pentium-Pro and above
x86-64 - 64-bit X86: EM64T and AMD64

因为我在 cmake -i 的时候有删掉一些不要的 targets,
所以这里可用的 targets 会比用默认值编 LLVM 的人少一些。
值得注意的是 llc 也有 C 跟 C++ 的 targets。
其实这个也不用太意外,
Open64 也是有 whirl2c 等一系列的工具。
不过相较于大部分 target 一改就要整套重编一份的compiler 来说,
LLVM 在这方面算是先进多了。
但其实这不过只是软件系统规划能力的差别罢了。

举个例子,
将 bitcode 编译成 ARM 的 assembly code:

llc -march=arm test.bc

这会得到 test.s。
不过当 target 是 c 或 cpp 时 suffix 就不是 .s。

 

原网页:http://blog.tinlans.org/2010/09/10/%E6%8B%96%E7%A8%BF%E5%BE%88%E4%B9%85%E7%9A%84-llvm-%E4%BD%BF%E7%94%A8%E6%96%87/

 

 类似资料: