LLVM开发插件以及遇到问题
程序员文章站
2024-03-23 20:14:10
...
LLVM
理解
什么是LLVM
- LLVM项目是模块化、可重用的编译器以及工具链技术的集合
- 创始人就说Swift之父
- LLVM本身不是首字母缩略词,它是项目的全名
传统的编译器架构
-
GCC、LLVM、Clang
- 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改
4.相比之下,GCC的前端和后端没分得太开,前端后端耦合在了一起,所以GCC为了支持一门新的语言,或者为了支持一个新的目标平台,就会变得特别困难
5.LLVM现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)
Clang
什么是Clang
1.LLVM项目的一个子项目
2.基于LLVM架构的C/C++/Objective-C编译器前端
相比于GCC,Clang具有如下优点
1.编译速度快:在某些平台下,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GCC快3倍)
2.占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
3.模块化设计: Clang采用基于库的模块化设计,易于IDE(开发工具)集成及其他用途的重用
4.诊断信息可读性强:在编译过程中,Clang创建并保留了大量详细的元数据,有利于调试和错误诊断
5.设计清晰简单,容易理解,易于扩展增强
IR–>Pass→IR属于中间代码,可以自己编写
Oc源文件的编译过程
词法分析
1.词法分析,生成Token:$ clang -fmodules -E -Xclang -dump-tokens main.m
语法树-AST
1.词法分析,生成语法树(AST): $ clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
- 词法分析完成之后,会把token拼接起来变成语法树
2.举例:
void test(int a, int b) {
int c = a + b - 3;
}
LLVM IR
1.LLVM IR有3种表示形式(但本质是等价的,就好比水可以有气体、液体、固件3种形态)
- text: 便于阅读的文本格式,类似于汇编语言,扩展名.ll ,$ clang -S -emit-llvm main.m
- memory:内存格式
- bitcode: 二进制格式,扩展名.bc $ clang -c -emit-llvm main.m
局部标识符以%开头
语法参考: https://llvm.org/docs/LangRef.html
开始制作
源码下载
1.xcode内置了一个clang,但是如果要自己制作,需要新下载一个编译
2.下载LLVM: git clone https://git.llvm.org/git/llvm.git/
3.下载clang:cd llvm/tools
git clone https://git.llvm.org/git/clang.git/
4.编译源码
- 安装cmake和ninja(先安装brew)
- brew install cmake
- brew install ninja(ninja如果安装失败,可以直接从github获取release版放入/usr/local/bin中 https://github.com/ninja-build/ninja/releases)
- 在LLVM源码同级目录下新建llvm_build
- cd llvm_build ; cmake -G Ninja …/llvm -DCMAKE_INSTALL_PREFIX=LLVM的安装路径 //(完成之后,如果llvm_build下有文件build.ninja说明成功,DCMAKE_INSTALL_PREFIX表示将来llvm编辑好的东西放置的位置建立放在LLVM源码同级目录下新建llvm_release)
- 依次执行编译、安装指令 ninja //编译完成后,llvm_build目录大概21.05G; ninja install //安装完毕后,目录大概11.92G
应用与实践
- libclang、libTooling
官方参考: htttps://clang.llvm.org/docs/Tooling.html
应用: 语法树分析、语法转换等 - Clang插件开发
官方参考
htttps://clang.llvm.org/docs/ClangPlugins.html
htttps://clang.llvm.org/docs/ExternalClangExamples.html
htttps://clang.llvm.org/docs/RAVFrontendAction.html
应用: 代码检查(命名规范、代码规范)等 - Pass开发
官方参考
htttps:/llvm.org/docs/WritingAnLLVMPass.html
应用: 代码优化、代码混淆等 - 开发新的编程语音
https://llvm-tutorial-cn.readthedocs.io/en/latest/index.html
https://kaleidoscope-llvm-tutorial-zh-cn.readthedoc.io/zh_CN/latest
clang插件开发-插件目录
- 首先在llvm/tools/clang/tools下新建立一个文件夹(如MJPlugin)
- 打开CMakeLists.txt,copy最后一句,把括号里名字改完自己建立的文件名
- 来到自定义文件夹目录下的控制台下: touch MJPlugin.cpp //插件都是用c++写的
- 再把CMakeLists.txt,copy一份到MJPlugin.cpp同级目录下,清空所有内容,加上如下内容
add_llvm_loadable_module(MJPlugin
MJPlugin.cpp
MJPlugin1.cpp
)
亲测上面方法不行,改成如下:
add_llvm_library(mj-plugin MODULE MJPlugin.cpp PLUGIN_TOOL clang)
if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
set(LLVM_LINK_COMPONENTS
Support
)
clang_target_link_libraries(mj-plugin PRIVATE
clangAST
clangBasic
clangFrontend
LLVMSupport
)
endif()
- llvm_build同级目录下新建立llvm_xcode;再在命令行执行: cmake -G Xcode …/llvm
之后会生成模版,xcode打开,在loadable modules下会看到mj_plugin,也可以鼠标选中target,键盘直接敲mj_plugin
会搜到mj_plugin,mj_plugin下有CMakeLists.txt和MJPlugin.cpp
打开MJPlugin.cpp进行代码编写,target选择MJPlugin进行编译
#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"
#include "llvm/Support/raw_ostream.h"
#include "clang/Sema/Sema.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/DeclObjC.h"
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
namespace MJPlugin {
class MJHandler: public MatchFinder::MatchCallback {
private:
CompilerInstance &ci;
public:
MJHandler(CompilerInstance &ci) : ci(ci) {}
// 找到之后,会调用handler的run方法
void run(const MatchFinder::MatchResult &Result) {
if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) { // ObjCInterfaceDecl类名
size_t pos = decl->getName().find('_'); // 拿到名字找下划线
if (pos != StringRef::npos) { // 一旦发现pos != 找不到 npos找不到的意思
DiagnosticsEngine &D = ci.getDiagnostics();
SourceLocation loc = decl->getLocation().getLocWithOffset(pos); // 位置->类声明的位置->下划线的位置 所以报错的时候会报错的准备的位置,并且下划线会是红色的
D.Report(loc,D.getCustomDiagID(DiagnosticsEngine::Error, "周周--类名中不能带下划线"));
}
}
}
};
class MJConsumer: public ASTConsumer {
private:
MatchFinder macther;
MJHandler handler;
public:
MJConsumer(CompilerInstance &ci) : handler(ci) {
// 构造函数,告诉matcher要找什么,找到了之后调用handler
macther.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
}
// 生成语法树之后会走这里
void HandleTranslationUnit(ASTContext &Ctx) {
cout << "MJPlugin-HandleTranslationUnit" << endl;
macther.matchAST(Ctx); // 查找语法树的节点,Ctx包含语法树信息
}
};
// 指定action后,会调用CreateASTConsumer方法
class MJAction : public PluginASTAction {
public:
unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
return unique_ptr<MJConsumer>(new MJConsumer(ci)); // 创建一个consumer,完成之后会生成语法树,
}
bool ParseArgs(const CompilerInstance &ci,const vector<string> &args) {
return true;
}
};
}
// 注册插件--指定action
static FrontendPluginRegistry::Add<MJPlugin::MJAction>
X("MJPlugin","The MJPlugin is my first clang-plugin.");
编写成功后,在Products文件夹下点击show in finder会看到同名动态库 // llvm_xcode/Debug/lib
clang插件开发-Hack Xcode
- 下载XcodeHacking,右键【HackedClang.xcplugin】点击"显示包内容"打开修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的内容
ExecPath = “” // 放编译好的clang的全路径 如 /Users/zhousuhua/Zhou_Zhou/llvm/llvm_build/bin/clang - XcodeHacking目录下的命令行下执行:
- 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
clang插件开发- Xcode 配置
- 在想要使用该插件的项目中→build setting 下搜索 other_c 找到Other C Flags
- 添加 _Xclang -load _Xclang 动态库绝对路径 _Xclang -add-plugin -Xclang 插件名称(MJPlugin)
- build setting 下搜索 compiler 找到 Compiler for C/C++/Objective-C
- 选择 Clang LLVM Trunk // 需要Hack Xcode
- 每次动态库更新,都需要clean一下重新编译
推荐阅读
-
LLVM开发插件以及遇到问题
-
如何清除SVN密码,以及重置eclipse中svn插件密码? 博客分类: 开发环境 eclipsesubversionsvn
-
如何清除SVN密码,以及重置eclipse中svn插件密码? 博客分类: 开发环境 eclipsesubversionsvn
-
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件_PHP
-
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件
-
ionic2自定义cordova插件开发以及使用(Android)
-
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件,遇到问题ecshop
-
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件_php实例
-
ionic2自定义cordova插件开发以及使用
-
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件,遇到问题ecshop_PHP教程