一、什么是LLVM
1、官网:https://llvm.org/
The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
LLVM项目是模块化、可重用的编译器以及工具链技术的集合
美国计算机协会 (ACM) 将其2012 年软件系统奖项颁给了LLVM,之前曾经获得此奖项的软件和技术包括:Java、Apache、 Mosaic、the World Wide Web、Smalltalk、UNIX、Eclipse等等
2、创始人
Chris Lattner,亦是Swift之父
3、有些文章把LLVM当做Low Level Virtual Machine(低级虚拟机)的缩写简称,官方描述如下
The name "LLVM" itself is not an acronym; it is the full name of the project. “LLVM”这个名称本身不是首字母缩略词; 它是项目的全名
二、传统的编译器架构
1.png- Frontend:前端
词法分析、语法分析、语义分析、生成中间代码 - Optimizer:优化器
中间代码优化 - Backend:后端
生成机器码
三、LLVM架构
2.png不同的前端后端使用统一的中间代码LLVM Intermediate Representation (LLVM IR)
- 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
- 如果需要支持一种新的硬件设备,那么只需要实现一个新的后端
- 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改
- 相比之下,GCC的前端和后端没分得太开,前端后端耦合在了一起。所以GCC为了支持一门新的语言,或者为了支持一个新的目标平台,就 变得特别困难
- LLVM现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)
四、Clang
1、什么是Clang?
LLVM项目的一个子项目
基于LLVM架构的C/C++/Objective-C编译器前端
官网:http://clang.llvm.org/
2、相比于GCC,Clang具有如下优点
- 编译速度快:在某些平台上,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GGC快3倍)
- 占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
- 模块化设计:Clang采用基于库的模块化设计,易于 IDE 集成及其他用途的重用 - - 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告
- 设计清晰简单,容易理解,易于扩展增强
3、Clang与LLVM
3.png
五、OC源文件的编译过程
1、命令行查看编译的过程:$ clang -ccc-print-phases main.m
4.png
2、查看preprocessor(预处理)的结果:
$ clang -E main.m
3、词法分析,生成Token:
$ clang -fmodules -E -Xclang -dump-tokens main.m
5.png
4、语法分析,生成语法树(AST,Abstract Syntax Tree):
$ clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
6.png
7.png
5、LLVM IR
- LLVM IR有3种表示形式(但本质是等价的,就好比水可以有气体、液体、固体3种形态)
text:便于阅读的文本格式,类似于汇编语言,拓展名.ll,
$ clang -S -emit-llvm main.m
memory: 内存格式
bitcode:二进制格式,拓展名.bc,
$ clang -c -emit-llvm main.m
6、IR基本语法
注释以分号 ; 开头
全局标识符以@开头,局部标识符以%开头
alloca,在当前函数栈帧中分配内存
i32,32bit,4个字节的意思
align,内存对齐
store,写入数据
load,读取数据
官方语法参考
https://llvm.org/docs/LangRef.html
六、源码
1、源码下载
下载LLVM
$ git clone https://git.llvm.org/git/llvm.git/
大小 648.2 M,仅供参考
下载clang
$ cd llvm/tools $ git clone https://git.llvm.org/git/clang.git/
大小 240.6 M,仅供参考
2、源码编译
-
安装cmake和ninja(先安装brew,https://brew.sh/)
$ brew install cmake $ brew install ninja
-
ninja如果安装失败,可以直接从github获取release版放入【/usr/local/bin】中 https://github.com/ninja-build/ninja/releases
-
在LLVM源码同级目录下新建一个【llvm_build】目录(最终会在【llvm_build】目录下生成【build.ninja】)
$ cd llvm_build $ cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=LLVM的安装路径
更多cmake相关选项,可以参考: https://llvm.org/docs/CMake.html -
依次执行编译、安装指令
$ ninja
编译完毕后, 【llvm_build】目录大概 21.05 G(仅供参考)
$ ninja install
安装完毕后,安装目录大概 11.92 G(仅供参考) -
也可以生成Xcode项目再进行编译,但是速度很慢(可能需要1个多小时)
-
在llvm同级目录下新建一个【llvm_xcode】目录
9.png
$ cd llvm_xcode $ cmake -G Xcode ../llvm
七、应用与实践
-
libclang、libTooling
官方参考:https://clang.llvm.org/docs/Tooling.html
应用:语法树分析、语言转换等 -
Clang插件开发
官方参考
https://clang.llvm.org/docs/ClangPlugins.html
https://clang.llvm.org/docs/ExternalClangExamples.html
https://clang.llvm.org/docs/RAVFrontendAction.html
应用:代码检查(命名规范、代码规范)等 -
Pass开发
官方参考:https://llvm.org/docs/WritingAnLLVMPass.html
应用:代码优化、代码混淆等 -
开发新的编程语言
https://llvm-tutorial-cn.readthedocs.io/en/latest/index.html
https://kaleidoscope-llvm-tutorial-zh-cn.readthedocs.io/zh_CN/latest/
八、clang插件开发
clang插件开发1 – 插件目录
1、在【clang/tools】源码目录下新建一个插件目录,假设叫做【ios-plugin】
11.png
2、在【clang/tools/CMakeLists.txt】最后加入内容: add_clang_subdirectory(ios-plugin),小括号里是插件目录名
12.png
3、在【ios-plugin】目录下新建一个【CMakeLists.txt】,文件内容是:add_llvm_loadable_module(IOSPlugin IOSPlugin.cpp)
IOSPlugin是插件名,IOSPlugin.cpp是源代码文件
13.png
4、编写插件源码
IOSPlugin.cpp
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
namespace IOSPlugin {
class IOSHandler : public MatchFinder::MatchCallback {
private:
CompilerInstance &ci;
public:
IOSHandler(CompilerInstance &ci) :ci(ci) {}
void run(const MatchFinder::MatchResult &Result) {
if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
size_t pos = decl->getName().find('_');
if (pos != StringRef::npos) {
DiagnosticsEngine &D = ci.getDiagnostics();
SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "M了个J:类名中不能带有下划线"));
}
}
}
};
class IOSASTConsumer: public ASTConsumer {
private:
MatchFinder matcher;
IOSHandler handler;
public:
IOSASTConsumer(CompilerInstance &ci) :handler(ci) {
matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
}
void HandleTranslationUnit(ASTContext &context) {
matcher.matchAST(context);
}
};
class IOSASTAction: public PluginASTAction {
public:
unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
return unique_ptr<IOSASTConsumer> (new IOSASTConsumer(ci));
}
bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
return true;
}
};
}
static FrontendPluginRegistry::Add<IOSPlugin::IOSASTAction>
X("IOSPlugin", "The IOSPlugin is my first clang-plugin.");
5、利用cmake生成的Xcode项目来编译插件(第一次编写完插件,需要利用cmake重新生成一下Xcode项目)
插件源代码在【Sources/Loadable modules】目录下可以找到,这样就可以直接在Xcode里编写插件代码
选择IOSPlugin这个target进行编译,编译完会生成一个动态库文件
6、在Xcode项目中指定加载插件动态库:BuildSettings > OTHER_CFLAGS -Xclang -load -Xclang 动态库路径 -Xclang -add-plugin -Xclang 插件名称
7、Hack Xcode
-
首先要对Xcode进行Hack,才能修改默认的编译器
下载【XcodeHacking.zip】,解压,修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的内容,设置一下自己编译好的clang的路径
14.png
- 然后在XcodeHacking目录下进行命令行,将XcodeHacking的内容剪切到Xcode内部
$ sudo mv HackedClang.xcplugin
xcode-select -print-
path`/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins
$ sudo mv HackedBuildSystem.xcspec xcode-select -print- path
/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications
`
8、修改Xcode的编译器
15.png
9、编译项目
编译项目后,会在编译日志看到IOSPlugin插件的打印信息(如果插件更新了,最好先Clean一下项目)
10、更多
想要实现更复杂的插件功能,就需要利用clang的API针对语法树(AST)进行相应的分析和处理
关于AST的资料
https://clang.llvm.org/doxygen/namespaceclang.html p https://clang.llvm.org/doxygen/classclang_1_1Decl.html p https://clang.llvm.org/doxygen/classclang_1_1Stmt.html
网友评论