LLVM(1)LLVM了解

一、什么是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

8.png

六、源码

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】目录
    $ cd llvm_xcode $ cmake -G Xcode ../llvm

    9.png

10.png

七、应用与实践

八、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.xcpluginxcode-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

九、推荐书籍

16.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • 什么是LLVM 官网:https://llvm.org/ LLVM项目是模块化、可重用的编译器以及工具链技术的集合...
    草原野马阅读 1,655评论 0 0
  • 什么是LLVM LLVM项目是模块化、可重用的编译器以及工具链技术的集合。 美国计算机协会 (ACM) 将其201...
    Coder_LRT阅读 2,360评论 0 1
  • 前言 今天是举国同庆的国庆假期的第一天,早上看到庄严的升旗激动坏了,中午都没睡觉,刷着朋友圈看着大家都在玩着,心想...
    MJBaby阅读 1,242评论 0 3
  • 开发的插件效果如下 简介 本开发是基于LLVM,那么我们先来简单了解一下LLVM: LLVM项目是模块化、可重用的...
    估唔到阅读 4,867评论 7 48
  • LLVM 简介 LLVM 全称是 Low Level Virtual Machine,它是源自 the Unive...
    juniway阅读 37,530评论 0 21