先CMake + vs2019 编译llvm和clang。在上一篇文章中已经记录的相应的流程。
内容如下
set CLANG_EXE=E:\workspace\llvm-project-master\clang\out\install\x64-Release\bin&
set PATH=%PATH%;%CLANG_EXE%;
上面那个TestClangPro 是我创建的目录,里面放一些测试的例子。
在文件窗口shift + 鼠标右键 "在此处打开命令行窗口" 运行当前设置的脚本
当前环境变量设置成功。这是一个cmd的环境变量,关闭此cmd窗口后自动清空当前设置的环境变量。
检查clang.exe 是否能引用 输入 clang --version
刚才创建的 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 ,结果如下:
以上打印的输出便是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的反汇编查看,相当于汇编代码,具体封装不予深究)
添加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 执行脚本,结果如下
进入TestClangPro下执行
clang -emit-llvm CompStream.cpp -S -o CompStream.ll
上诉命令将生成IR的可读格式,下面通过Notepad++ 打开。直接定位main语句。
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 语法分析