47f3410abedc2beb58b54eaa37ceb346.png声明:本文来自于微信公众号 InfoQ(ID:infoqchina),作者:赵钰莹,授权站长之家转载发布。

闭源时被质疑是否真的存在这样一个编译器,开源后又被喷技术含量不行,方舟编译器怎么就这么难?本文,鸿蒙开源主管及方舟编译器架构师首次完整公开分享了方舟编译器的基础架构。

1方舟编译器怎么这么难?

自 8 月 31 日正式开源,方舟编译器的讨论热度达到高潮,知乎话题《如何看待方舟编译器于 2019 年 8 月 31 日开源?》累计被浏览了五百多万次,网友共计发表了八百多条评论。由于本次开源放出的代码较少,不少网友都抛出了疑问,比如运行时方面的计划,开源的原因,开发出来的应用是否只能在华为手机上运行以及未来打算怎么做生态等,十年磨出的方舟编译器,一朝开源怎么就面临这么艰难的境地呢?

事实上,相比于过往十年的研发历程,现在的处境可能还算不上最艰难的。

2009 年,华为第一个编译团队成立,起初是为了无线基站领域的 DSP 性能问题。

2014 年,Open64 的鼻祖 Fred Chow 加入,这对华为后来的编译器发展,包括如今的方舟编译器都产生了重要影响。

2017 年,华为手机的销量非常之高,又开始出现大量新的问题。当时,整个混合执行模式里,解释执行的占比非常高,而执行性能比较低,JIT 在后台生成 JIT 代码的过程又会消耗大量 CPU 资源,编一个函数需要的时间也很长。此外,由于优化不完善,虚拟机的停顿时间非常长,尤其内存不足时,会导致大量卡顿。

基于上述原因,整个团队讨论出两条可行的解决方案:一是在现有虚拟机的基础上修改;二是另起炉灶,重新做一套能够执行 Java 的运行环境和编译器。第一条路相对简单省事,但只能解决部分问题,华为最终选择了第二条路线,这就促成了如今方舟编译器的诞生。

在方舟编译器的设计上,Fred Chow 的一篇论文提供了很好的思路:基于统一的 IR 既支持多种编程语言表示,又支持后端多芯片代码的指定形成。这就构成了方舟编译器的理论基础。在这个理论基础上,方舟编译器团队基于 MAPLE IR 做了更复杂的优化和更广义的控制流分析。

2019 年 4 月份,华为发布方舟编译器(ArkCompiler),同时在 8 月底将其编译框架代码开源,并计划后续完整开源方舟编译器的所有代码。然而,开源后的方舟编译器受到了四面八方开发者的高度关注,面对现在开源出来的少量代码,很难让人相信这是一个多语言、跨平台、高效的编程环境。

于是,整个团队首次公开了方舟编译器的基础架构和源码分析。

2方舟编译器源码分析

8 月 31 日,华为方舟编译器开源了编译器框架部分源码,包括编译器中间表示(IR,Intermediate Representation)和语言编译实现,同时搭配编译器其它二进制组件,实现 Java 程序到 aarch64 汇编指令的编译过程。

a498a4cca3cf72766721fb17ef94a728.png

在华为的描述中,开发者可基于开源代码和二进制,编译构建出编译器工具链,尝试对 Java 程序进行编译。社区参与者可以通过框架源码学习方舟编译器的编译器中间表示(IR)及基本的中端编译框架,熟悉方舟编译器的架构思想,并参与诸如对编译器中端优化的贡献。方舟编译器是为支持多种编程语言、多种芯片平台的联合编译、运行而设计的统一编程平台,包含编译器、工具链、运行时等关键部件。

就目前可见的范围,整个方舟编译器的开源部分代码由 C、C++ 的头文件、源码和汇编代码组成,C 语言的头文件占了绝大部分,目前提供的代码一共有七万多行,注释大概有一万多行,还有很多空行,加起来大约有 10 万行代码,而其中的注释非常之少。每个文件的大小整体也不是很大,个别超过了 2000 多行,大部分在 1000 行以下。

从代码内容来看,主要是与中间代码相关的部分,当然也包括了 Phase、IPA,但实际里面主要是阶段性管理的辅助代码,真正相关的代码目前还没有呈现。huawei_secure_c 的文件中存放了大量关键代码,比如内存拷贝等关键函数。根据选取的几个测试用例,目前对 Java 1.6 及以上版本的支持都没有问题,从 Java 到 IR 的翻译还是比较顺畅,只不过在第三库版本支持上可能会有问题。

至于上文提到的中间代码,根据 MAPLE 的文档,主要有如下几个特点:尽可能保留源代码信息;

高层次树状层次化结构;

低层次与指令一一对应;

可扩展——支持新的语言和控制结构。

方舟基础架构与 IR 中间表示

目前开源的方舟基础架构一部分是关于 Java/Kotlin 的编程语言实现,因为这两类语言强依赖虚拟机,方舟编译器去掉了虚拟机之后,补全了一些功能在里面;另一部分是后续优化和分析的基础支持,比如开源了 SSA 的表示。

303d750ff202b8ea1adfc23568a9cb0c.png

方舟编译器架构师在近期的分享中表示,Maple IR 设计时基本考虑了三件事情:

1、IR 存在于三种不同的格式中:Binary,主要用来做分发,或者是真正执行时需要考虑效率问题;

中间语言需要有可读性,因为程序员很关心代码的执行过程;

存在 In-memory 的过程,也就是组织中间语言在内存的存储

2、分层设计,Maple 的整个想法来源于 Open64 的鼻祖 Fred Chow,而 Open64 当时的设计是五层结构。方舟编译器总结和借鉴了 Open64 的经验,形成了一个分层的设计,按需来选择不同层次。这样可以让高层语言快速回到原代码,大多数编译器都会支持这一功能,很多的应用场景都可用到。此外,高层的中间表示对实现接近原语言的优化会比较方便。比如,方舟编译器保留了较高层 Class 类型,这样做 TBAA 和 Devirtual 相对容易。

3、高层结构更接近原语言。原语言的代码最为精简,方舟编译器希望中间结构也能够更加精简,比如分发格式更小。同时,希望能够重用编译优化能力,当引入新语言时,能够尽量减少改动。分层之后引入新语言,可以只改高层,高层拓展后,底层所有优化保持不变。

在中间语言的部分,方舟编译器架构师表示可分为两部分看待:类型和操作符。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐