Clang
插件
在编译时,Clang
插件可运行
额外的用户定义操作
.
介绍
Clang
插件在代码上运行FrontendActions
.见FrontendAction
教程,了解如何使用RecursiveASTVisitor
编写FrontendAction
.
这里,演示如何编写
简单的clang
插件.
编写PluginASTAction
插件
与编写普通FrontendActions
的主要区别在,可处理插件命令行选项
.PluginASTAction
基类声明一个必须在插件
中实现的ParseArgs
方法.
bool ParseArgs(const CompilerInstance &CI,const std::vector<std::string>& args) {for (unsigned i = 0, e = args.size(); i != e; ++i) {if (args[i] == "-some-arg") {//处理命令行参数.}}return true;
}
注册插件
在运行时
,编译器从动态库
加载插件
.要在库中注册
插件,请使用FrontendPluginRegistry::Add<>
:
static FrontendPluginRegistry::Add<MyPlugin> X("my-plugin-name", "my plugin description");
定义编译指示
插件
还可通过声明PragmaHandler
并使用PragmaHandlerRegistry::Add<>
注册它,来定义编译指示:
//定义`#pragma example_pragma`的编译指示处理器
class ExamplePragmaHandler : public PragmaHandler {
public:ExamplePragmaHandler() : PragmaHandler("example_pragma") { }void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &PragmaTok) {//处理编译指示}
};
static PragmaHandlerRegistry::Add<ExamplePragmaHandler> Y("example_pragma","example pragma description");
定义属性
插件可通过声明ParsedAttrInfo
并使用ParsedAttrInfoRegister::Add<>
注册它来定义
属性:
class ExampleAttrInfo : public ParsedAttrInfo {
public:ExampleAttrInfo() {Spellings.push_back({ParsedAttr::AS_GNU,"example"});}AttrHandling handleDeclAttribute(Sema &S, Decl *D, const ParsedAttr &Attr) const override {//处理属性return AttributeApplied;}
};
static ParsedAttrInfoRegistry::Add<ExampleAttrInfo> Z("example_attr","example attribute description");
插件属性
必须定义的ParsedAttrInfo
的成员包括:
1,拼写(Spellings)
,必须用属性
的每个,都由属性语法
及该语法的属性名
拼写方式组成的拼写
这里填充.
如果语法
允许域,则拼写
必须为"scope::attr"
(如果有域)或"::attr"
(如果没有).
2,handleDeclAttribute
,这是应用属性到声明
的函数.它负责检查属性
参数是否有效
,且一般给Decl
添加Attr
来应用
属性.
它返回AttributeApplied
(指示已成功应用属性
)或(失败)AttributeNotApplied
.
根据属性
,可能要定义的ParsedAttrInfo
成员包括:
1,NumArgs
和OptArgs
,设置属性的必需
参数和可选
参数个数.
2,diagAppertainsToDecl
,检查
属性是否用在正确
的声明类型上,如果没有,则发出诊断
.
3,diagLangOpts
,检查当前语言模式
是否允许该属性
,如果禁止,则发出诊断
.
4,existsInTarget
,检查
给定目标
是否允许该属性
.
Attribute.cpp
示例,可查看属性插件的工作示例.
放在一起
用打印顶级
函数名来示例插件
.此例已签入clang
仓库;请查看最新版本的PrintFunctionNames.cpp
.
运行插件
用编译器驱动
Clang
驱动接受-fplugin
选项来加载插件.Clang
插件可通过fplugin-arg-<pluginname>-<argument>
选项,从编译器驱动
命令行接收参数
.
这样,插件名
自身不能包含破折号
,但传递
给插件的参数可以.
$ export BD=/path/to/build/directory
$ make -C $BD CallSuperAttr
$ clang++ -fplugin=$BD/lib/CallSuperAttr.so \-fplugin-arg-call_super_plugin-help \test.cpp
如果插件名
包含破折号
,请重命名
插件或使用下面列举的cc1
命令行选项.
使用cc1
命令行
要运行插件,必须通过-load
命令行选项,加载
包含插件注册表
的动态库.这加载
所有已注册
的插件,可通过指定-plugin
选项来选择
要运行的插件
.
可用-plugin-arg-<plugin-name>
传递插件
的其他参数
.
注意,这些选项必须到达clang
的cc1
进程.有两个方法:
1,用-cc1
选项直接调用解析
过程;缺点
是没有配置默认头文件搜索路径
,因此要在命令行
上指定完整
的系统路径配置
.
2,如常使用clang
,但在cc1
进程的所有参数
前面加上-Xclang
.
如,要对clang
中的源文件
上运行print-function-names
插件,请先构建
该插件,然后用源码树
中的插件调用clang
:
$ export BD=/path/to/build/directory
$ (cd $BD && make PrintFunctionNames )
$ clang++ -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE \-I$BD/tools/clang/include -Itools/clang/include -I$BD/include -Iinclude \tools/clang/tools/clang-check/ClangCheck.cpp -fsyntax-only \-Xclang -load -Xclang $BD/lib/PrintFunctionNames.so -Xclang \-plugin -Xclang print-fns
另见print-function-name
插件示例的README
这里
使用clang
命令行
在clang
命令行上,使用-fplugin=plugin
,在cc1
命令行上,按-load
参数传递插件
.如果插件
类实现了getActionType
方法,则会自动运行插件
.
如,要在主AST
操作后自动
运行插件(即与用-add-plugin
相同):
//在主`AST`操作后自动运行插件
PluginASTAction::ActionType getActionType() override {return AddAfterMainAction;
}
与-clear-ast-before-backend
的交互
为了减少编译器的内存使用峰值
,建议在一般是生成代码
的主操作
前,运行插件
.这是因为在codegen
操作后运行
的插件
,都会自动关闭-clear-ast-before-backend
.
-clear-ast-before-backend
通过在生成IR
后和运行IR
优化前,清理ClangAST
来减少峰值内存.按getActionType
,使用CmdlineBeforeMainAction
或AddBeforeMainAction
来运行插件,同样受益于-clear-ast-before-backend
.
插件
必须确保不修改AST
,否则应在主操作
之后运行它们.