【openEuler 2023 年度优秀项目推荐】openEuler Compiler SIG 编译器插件框架项目

-
描述:PINC (PlugIN framework for Compiler)是在openEuler社区发起的创新项目,帮助开发者无需深入修改编译器内部逻辑,可实现独立编译优化和编译工具的便捷开发。
-
推荐人:李彦成@li-yancheng openEuler Compiler SIG maintainer,
黄晓权@huang-xiaoquan openEuler Compiler SIG committer,
丁光亚@dguangya openEuler Compiler SIG committer,伍明川@wumingchuan openEuler Compiler SIG committer
-
社区地址:
https://gitee.com/openEuler/pin-gcc-client
https://gitee.com/openeuler/pin-server
openEuler
优秀项目衡量标准
-
推荐奖项 openEuler
年度优秀项目, openEuler
年度技术创新项目。
- 开源开放
项目代码托管在openEuler。
-
行业影响 在开源领域顶会OSSEU23上做主题演讲,致力于为开发者提供个性化编译的便捷开发。
-
技术创新 插件框架提供常用编译插件服务,让硬件厂商迅速使能和构建生态;插件开发基于MLIR技术进行,可处理不同编译器IR的转换,一次开发可多编译器使用;插件服务与编译进程隔离运行,插件逻辑可与编译器解耦,独立研发和部署。
-
高质量开发和运营 代码符合规范,代码质量高,对用户反馈问题响应及时。
1.
软件介绍
现如今,主流的编译器框架都有提供自己的插件式开发能力,方便开发人员可以在不修改编译器的情况为编译器添加新的功能。例如GCC Plugin,它是GCC 4.5.0开始引入的一项插件功能,允许用户通过插件来干预GCC的编译过程并获取GCC编译过程中的各种数据,进而可以修改编译过程中生成的中间数据。
GCC Plugin使开发人员可以通过回调函数,在编译器各个阶段添加新功能,并且不需要去修改编译器本身。这样的插件式开发提供了如下优势:
1)
缩短了构建和测试新功能所需的时间。只需要编译实现新功能所需的代码,不需要重新编译GCC。
2)
简化了开发成本。对那些需要修改GCC但没有时间或倾向去深入探讨编译器内部的开发人员,提供了入门难度低、更高效的开发方式。
但是,由于编译器插件是完全基于各自编译器框架进行开发实现的,与具体编译器版本紧耦合。由此引发了编译器插件的两大主要挑战。
挑战一:重复构建引发的开发维护成本
2021年在Linux内核社区中有过对GCC插件的讨论。近年来,内核自我保护项目(Kernel
Self Protection Project)将一些grsecurity/PaX的patch以GCC插件的形式给予Kernel诸多加固支持。但是,由于这些GCC插件与GCC版本联系非常紧密,一旦Kernel使用的GCC发生版本变动,这些加固插件均需要对每一次变动的GCC版本进行适配,由此给插件维护者带来了大量的维护工作量,如下图所示。
同时,类似完整性校验、兼容性检测等各个插件均会涉及的公共功能,相同的功能却需要在不同的插件里重复开发和维护(如下图)。这样给插件开发者带来了重复的开发维护成本。
挑战二:不同编译器生态引发的插件重复开发维护成本
当前存在两款主流的编译器框架,GCC使用面广,稳健发展性能较好;LLVM开拓创新,发展迅速。市场上大量的编译工具和编译扩展能力,都是基于这两类编译器完成的。这导致任何编译工具需要选择两种编译框架之一或者重复进行开发。
因为编译器框架的不同,导致即使是相同的工具设计逻辑,代码几乎没办法复用。这需要在不同的编译器框架上做重复的代码开发并分别进行维护,直接拉高了工具的开发和维护成本。
如上图所示,Randstruct作为Kernel加固工具,于2017年基于GCC插件实现,然而在隔年,为了能在LLVM上使能此工具,基于Clang插件重复进行开发,但是基本功能和逻辑其实并没有太大变化。时至今日,仍然存在一个插件工具要为两个编译器生态维护两个不同代码版本的情况。
PINC:编译器插件框架
为了解决上诉编译器插件的两大挑战,我们设计实现了编译器插件框架(PINC:PlugIN Framework for
Compiler)。
如上图所示,我们的目标是让基于编译能力的相关工具开发者只需要做一次开发,即可在多个不同编译器框架落地;同时作为开发平台,提供对公共能力的支持与维护。
编译器插件框架的总体架构如下图所示:
编译器插件框架的最大架构特点就是设计插件服务端组件和编译插件客户端模块组成代理模式。插件服务端只承载插件逻辑,开发者在服务端使用相对中立的MLIR以及我们提供的一系列插件API进行开发,将关注点聚焦于工具的设计逻辑。服务端与不同编译器的客户端对接,并通过跨进程通信传递IR数据与操作,将插件逻辑操作转换映射,执行在客户端编译器上,实现一份代码,多编译器落地的目的。
插件使用者只需要下载所需插件的库文件和校验文件,通过框架的配置文件即可与编译器客户端联合使能。插件客户端将作为GCC/LLVM Plugin进行加载,拥有其完全不需要修改GCC编译器代码实现新功能的优势,能够灵活快捷地使用。同时享受框架所维护的各种公共能力。
下图是编译器插件框架的架构图。
编译器插件框架作为开发平台,提供了诸多公共能力的统一支持与维护。
・
日志记录功能:在服务端和客户端,都支持日志记录功能。插件执行过程中可以打印内部结果到日志,记录插件执行过程关键的执行信息,供用户调试和查阅。日志记录提供ERROR、WARN、INFO和DEBUG4个级别的信息配置能力,可以通过配置管理能力由用户进行设置。
・
运行监控器:在跨进程通信过程中,插件框架提供运行监控器对安全编译选项以及操作合法性进行校验。
・
兼容性校验:在插件客户端,我们提供了对插件版本兼容性的校验功能。
・
完整性校验:服务端插件工具提供二进制签名与完整性校验能力,防止对插件工具的恶意修改。
此时,我们能够发现,为了实现一次开发,多编译器落地的目标,工具开发所使用的IR应该足够中立,并且拥有高效翻译转换到其他编译器IR的能力。为此,编译器插件框架提供PluginDialect给插件开发者使用,并选择基于MLIR进行设计实现。
针对前面提到的挑战,重复构建引发的开发维护成本。如果基于编译器插件框架开发实现,由于框架插件逻辑与具体编译器解耦的设计理念,可以极大减少插件工具维护者所需要维护的版本数量,明显减少维护工作量。
案例分析
以查询元素扩展优化(Array Widen Compare)在编译器插件框架上的实现作为案例,介绍编译器插件框架的优势。
查询元素扩展优化的效果如上图所示。该优化来源于GCC for openEuler的优化特性,是用宽数据类型对原数组指针解引用,达到一次比较多个元素的效果。整个优化在GCC上实现的时候,大约是1人月的工作量。由于是专用于GCC,没有办法直接移植到LLVM等其他编译器。如果想要在LLVM上使能,需要重新开发,至少需要额外的1人月。
为此,选择基于PINC进行移植。从GCC上的查询元素扩展优化移植到编译器插件框架上,仅用了7人天。依托编译器插件框架实现了优化与GCC解耦,有快速使能LLVM的机会。并且编译器插件框架自身所维护的兼容性检测、完整性校验等公共能力,也能让插件开发者直接使用,降低了开发者的维护成本。
项目未来计划
编译器插件框架已在openEuler社区开源,插件服务端在openEuler社区的代码仓链接:https://gitee.com/openeuler/pin-server,GCC客户端的链接:https://gitee.com/openEuler/pin-gcc-client,LLVM客户端也即将在openEuler社区开源。欢迎大家使用、讨论、一起贡献。
在未来,我们专注于进一步提高插件API的功能和IR转换的覆盖率。针对插件易用性和调试便捷性,和社区贡献者一起讨论,提高编译器插件框架的客户体验。当前编译器插件框架的客户端均基于GCC等编译器自身的插件系统构建,因此只能处理中端优化。未来,编译器插件框架也将着力构建覆盖前端、后端、链接等阶段的插件体系,扩大作用范围。
2.
社区大事记
-
2022 openEuler Summit
技术分论坛 --
编译器插件框架
- 2023 openEuler Developer Day --
编译器插件框架亮相展台
- 2023
开放原子全球开源峰会
?C
编译器插件框架亮相展台
- 2023
Open Source Summit Europe主题演讲 -- The Compiler Plugin Framework to Facilitate Customized Compilation and Development
-
wumingchuan (A)