LLVM编译原理
LLVM
LLVM是架构编译器的框架系统,以C++编写而成,用于优化任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time)。对开发者保持开放,并兼容已有脚本。
传统编译器设计
源码 Source Code + 前端 Frontend + 优化器 Optimizer + 后端 Backend(代码生成器 CodeGenerator)+ 机器码 Machine Code,如下图所示
ios的编译器架构
OC、C、C++使用的编译器前端是Clang,Swift是swift,后端都是LLVM,如下图所示
模块说明
前端 Frontend:编译器前端的任务是解析源代码(编译阶段),它会进行 词法分析、语法分析、语义分析、检查源代码是否存在错误,然后构建抽象语法树(Abstract Syntax Tree AST),LLVM的前端还会生成中间代码(intermediate representation,简称IR),可以理解为llvm是编译器 + 优化器, 接收的是IR中间代码,输出的还是IR,给后端,经过后端翻译成目标指令集
优化器 Optimizer:优化器负责进行各种优化,改善代码的运行时间,例如消除冗余计算等
后端 Backend(代码生成器 Code Generator):将代码映射到目标指令集,生成机器代码,并且进行机器代码相关的代码优化
示例代码:
#define add(a, b) a + b
int test(int a, int b)
{
return a + add(b, 10);
}
int main()
{
int a = test(1, 2);
return 0;
}
一、预处理编译阶段
这个阶段主要是处理包括宏的替换,头文件的导入,可以执行如下命令,执行完毕可以看到头文件的导入和宏的替换
//在终端直接查看替换结果
clang -E main.m
//生成对应的文件查看替换后的源码
clang -E main.m >> main2.m
需要注意的是:
-
typedef
在给数据类型取别名时,在预处理阶段不会被替换掉
-
include在这部分中执行,执行后会引入对应的声明
#include 实质是什么?
预编译的时候copy include头文件的内容到当前行
被#include的header file中最常见的内容分为哪几类?
宏定义
typedef
包含别的头文件
inline函数定义
函数声明
struct,union,enum类型定义
(其实可以打开一个.h文件来看看,如 /user/include/stdio.h)
# 1 "main.m"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 412 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.m" 2
int test(int a, int b)
{
return a + b + 10;
}
int main()
{
int a = test(1, 2);
return 0;
}
二、编译阶段
编译阶段主要是进行词法、语法等的分析和检查,然后生成中间代码IR
1、词法分析
预处理完成后就会进行词法分析
,这里会把代码切成一个个token
,比如大小括号、等于号还有字符串等,
- 可以通过下面的命令查看
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
词法分析结果
int 'int' [StartOfLine] Loc=<main.m:2:1>
identifier &