LLVM 的编译框架基本流程探索(Clang)

先CMake + vs2019 编译llvm和clang。在上一篇文章中已经记录的相应的流程。

创建clangSetEnvPath.cmd

LLVM 的编译框架基本流程探索(Clang)_第1张图片

内容如下

set CLANG_EXE=E:\workspace\llvm-project-master\clang\out\install\x64-Release\bin&
set PATH=%PATH%;%CLANG_EXE%;

上面那个TestClangPro 是我创建的目录,里面放一些测试的例子。

在文件窗口shift + 鼠标右键 "在此处打开命令行窗口" 运行当前设置的脚本

LLVM 的编译框架基本流程探索(Clang)_第2张图片

当前环境变量设置成功。这是一个cmd的环境变量,关闭此cmd窗口后自动清空当前设置的环境变量。

检查clang.exe 是否能引用 输入 clang --version

LLVM 的编译框架基本流程探索(Clang)_第3张图片

查看clang的编译流程

刚才创建的 TestClangPro 文件夹下 创建文件CompStream.cpp ,尽量写得简单一些,后面AST语法树可能还会用到,代码如下:

#include 

int main()
{
    int a = 10;
    int b = 20;
    int c = a + b;

    std::cout<< c <

执行语句 clang -ccc-print-phases CompStream.cpp  ,结果如下:

LLVM 的编译框架基本流程探索(Clang)_第4张图片

以上打印的输出便是clang编译的整个流程。

 

解析输出文本

0: input, "CompStream.cpp", c++              //这里输入文件CompStream.cpp 识别格式C++
1: preprocessor, {0}, c++-cpp-output         //预处理
2: compiler, {1}, ir                         //译码为 IR 格式
3: backend, {2}, assembler                   //传递给后端生成汇编代码
4: assembler, {3}, object                    //汇编代码生成目标文件
5: linker, {4}, image                        //连接成可执行文件


以上clang的操作正好对应 gcc的几个编译流程

1、预处理:gcc -E project.c -o project.i     //生成预处理文件   (clang 0-2)
2、编译:  gcc -S project.i -o project.S     //生成汇编文件     (clang 3)
3、汇编:  gcc -c project.S -o project.o     //生成机器语言     (clang 4)
4、链接:  gcc project.o -o project          //生成可执行程序   (clang 5)

上面讲gcc 和clang进行了对比,发现clang 拥有更多的流程 包含IR的中间码这种格式,这也是llvm的典型特征:

文件 =》前端=》IR =》后端 =》可执行程序

那么这个IR是个什么东西?

这是链接前后端中间格式。LLVM的体系结构就不介绍了。百度一大堆。

简而言之,前端实现语言识别,后端实现代码平台适应。而IR则是链接前后端的通用约定。IR存在两种查看格式

1.可阅读的格式

2.内部格式 (可通过exe的反汇编查看,相当于汇编代码,具体封装不予深究)

IR格式文件查看

添加llvm环境到 clangSetEnvPath.cmd (LLVM也是vs2019编译安装的)

set CLANG_PATH=E:\workspace\llvm-project-master\clang\out\install\x64-Release\bin&
set LLVM_PATH=E:\workspace\llvm-project-master\llvm\out\install\x64-Release\bin&
set PATH=%PATH%;%CLANG_PATH%;%LLVM_PATH%;

关闭之前的cmd,shift + 鼠标右键打开cmd 执行脚本,结果如下

LLVM 的编译框架基本流程探索(Clang)_第5张图片

进入TestClangPro下执行

clang -emit-llvm CompStream.cpp -S -o CompStream.ll

LLVM 的编译框架基本流程探索(Clang)_第6张图片

上诉命令将生成IR的可读格式,下面通过Notepad++ 打开。直接定位main语句。

LLVM 的编译框架基本流程探索(Clang)_第7张图片

IR文件解析

; Function Attrs: noinline norecurse optnone uwtable
define dso_local i32 @main() #0 {     定义了一个函数 __int32 main()
  %1 = alloca i32, align 4            开辟空间 %1的的变量 align 4bit 长度对齐
  %2 = alloca i32, align 4            开辟空间 %2的的变量
  %3 = alloca i32, align 4            开辟空间 %3的的变量
  %4 = alloca i32, align 4            开辟空间 %4的的变量
  store i32 0, i32* %1, align 4       0  保存到 %1 
  store i32 10, i32* %2, align 4      10 保存为 %2
  store i32 20, i32* %3, align 4      20 保存为 %3
  %5 = load i32, i32* %2, align 4     %5 《= %2      这里可能是load到加法器
  %6 = load i32, i32* %3, align 4     %6 《= %3
  %7 = add nsw i32 %5, %6             %7 《= %5 + %6
  store i32 %7, i32* %4, align 4      %7 =》 %4      从加法器取出值到当前main函数栈
  %8 = load i32, i32* %4, align 4     %8 《= %4      值传入形参 %8为std::cout(int)中参数
  %9 = call dereferenceable(112) %"class.std::basic_ostream"* @"??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z"(%"class.std::basic_ostream"* @"?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A", i32 %8)     
                                      调用 class.std::basic_ostream(%8) 来自函数重载<<
                                      
                                    
  %10 = call dereferenceable(112) %"class.std::basic_ostream"* @"??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z"(%"class.std::basic_ostream"* %9, %"class.std::basic_ostream"* (%"class.std::basic_ostream"*)* @"??$endl@DU?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@@Z")
                                       调用std::endl();
  ret i32 0                            返回值 __int32 的值0 常量区
}

可能跟咱们预想中的代码流程有差异,不过也不难理解,毕竟电脑不像人脑那么智能

 

其他指令

clang -E src.cpp       输出预处理结果

clang -fmodules -E Xclang -dump-tokens src.cpp               关键字分析(词法分析)

clang -fmodules -fsyntax-only -Xclang -ast-dump src.cpp   语法分析

 

 

你可能感兴趣的:(LLVM)