作者:xianyuqiang 编译器首席架构师

ArkCompiler(方舟编译器)是组件化、可配置的多语言编译和运行平台,它既能撑持繁多语言运行环境,也能撑持多种语言组合的运行环境。它目前次要反对的语言是JavaScript、TypeScript和Java。

一、概述
HarmonyOS的设计指标,是成为买通手机、PC、平板、电视、车机和智能穿戴等多种设施的对立操作系统。

                                    图1 多设施互联

其利用开发有多编程语言、多范式的反对需要,其中高级编程语言包含JavaScript、TypeScript、Java等,开发范式包含申明式UI范式、分布式编程范式。咱们须要相应的编译器和运行时来撑持这些高级利用编程语言的高效开发、部署和运行。使利用开发者能应用同一套开发框架实现一次开发多端部署运行。并且让应用HarmonyOS设施的用户,能取得对立的用户体验。于是,ArkCompiler应运而生。

  1. 指标

ArkCompiler是为反对多种编程语言、多种芯片平台的联结编译、运行而设计的对立编程平台,其设计指标是提供一个语言可插拔、组件可配置的多语言编译器运行时。

语言可插拔:设计架构上反对多种语言接入,ArkCompiler有能力提供具备高效执行性能且具备跨语言劣势的多语言运行时,也能够在小设施上提供高效轻量的繁多语言运行时。
组件可配置:ArkCompiler具备丰盛的编译器运行时组件零碎。通过定制化配置编译运行时的语言和组件,以反对手机、PC、平板、电视、汽车和智能穿戴等多种设施上不同的性能和内存需要。

  1. 架构

如图2所示,ArkCompiler蕴含编译器、工具链、运行时等关键部件。ArkCompiler工具链实现对应语言的前端编译器,将前端开发框架的高级语言编译成对立的字节码/二进制文件。依据不同的利用场景,通过ArkCompiler运行时解释器解释执行字节码文件或JIT/AOT编译器编译执行对应体系架构的优化机器码,从而晋升运行效率和启动性能。

                               图2 ArkCompiler运行原理

上面,本文将从前端编译器,运行时开展介绍。

二、前端编译器
前端编译器是高级语言通往语言运行时的桥梁,它依照语言标准,将编程语言表白的语义翻译为运行时可能了解的介质,在ArkCompiler解决方案里,这体现为ArkCompiler字节码。即图3中的ArkCompiler Bytecode(简称abc)。局部语言,也反对通过ArkCompiler的AOT Compiler组件间接将字节码编译成对应体系架构的优化机器码。

                                 图3 ArkCompiler前端
  1. 前端编译器性能

在须要反对多种语言的ArkCompiler中,前端编译器的次要作用是在Host侧把源码生成字节码文件,这样的长处:

利用Host弱小的计算能力,可能在运行前做更多更简单的算法优化,缩小运行时的工作,进步运行效率。
相比常见的JavaScript运行时,能够把端侧的编译解析过程提前到公布前,晋升程序的启动性能。

                               图4 JavaScript运行流程

编译优化

ArkCompiler提供对TypeScript(TS)的原生反对。在前端编译TS源码时,会利用TS的显式类型申明,利用类型推导进行类型优化,并且将推导出的类型信息通过字节码文件保留至运行时,由此运行时能够间接利用类型信息执行疾速门路。此外,动态的类型剖析和推导也使得TS AOT (Ahead of Time) Compiler成为可能,动态剖析失去的类型信息帮忙AOT Compiler间接编译生成高质量的机器码,使得TS源码能够间接以机器码模式运行,进一步晋升运行性能。

                                      图5 编译优化
  1. ArkCompiler字节码

ArkCompiler字节码(ArkCompiler Bytecode)是运行时解释器可能解析运行的一种硬件和平台无关的两头表现形式,以紧凑、可扩大、多语言反对作为设计指标。屏蔽设施的差别,反对利用的跨设施散发、部署和运行。ArkCompiler采纳的是基于寄存器的字节码格局。每个寄存器的宽度为64位,最多反对65536个寄存器。

(1)寄存器

ArkCompiler寄存器要求可能搁置对象援用和根本类型,宽度采纳64位。寄存器的作用域是以函数栈帧为范畴。在字节码指令编码中,寄存器索引反对4位、8位以及16位的变长编码,在反对办法内不同数量范畴的寄存器寻址的同时减小字节码尺寸。

(2)累加寄存器

累加寄存器,俗称累加器,是一个非凡的寄存器,被指令隐含应用。应用累加器的次要目标是在不损失性能的前提下改善指令编码密度。在ArkCompiler字节码中,上一条指令利用累加器作为后果输入,下一条指令将此累加器作为输出,能够无效改善指令密度,减小字节码的尺寸。同时,通过在生成字节码阶段的数据流及控制流剖析和优化,前端编译器能够无效打消冗余的累加器load和store操作。

(3)根本类型反对

ArkCompiler字节码提供对32位(i32)和64位(i64)整型数值的寄存器操作反对,8位和16位数值通过扩大到32位来模仿。反对对IEEE-754双精度浮点f64值的寄存器的操作,f32数据类型(IEEE-754单精度)也通过转换为f64值进行模仿。根本数据类型不须要虚拟机进行记录、跟踪和推导,而是通过操作不同根本数据类型的专用字节码进行示意,包含整数值的符号性。为了更无效地利用字节码的指令空间,设计中对高频应用的数据类型和操作引入更多的专用字节码,而对低频应用的数据类型和操作采纳更通用的字节码。

(4)语言相干类型反对

ArkCompiler依据其执行的语言反对层次化的类型零碎。这样,创立或者从常量池加载的字符串、数组、异样对象等,都是含有相应档次关系的、和具体语言标准相匹配的数据对象。

(5)动静类型语言反对

为反对相似JS/TS的动静类型语言,ArkCompiler通过非凡的标记值("Any")示意动静类型值,其包装了值自身和相应的类型信息(包含根本类型和对象援用类型数据)。虚构寄存器的宽度能够包容“Any”值。同时,在动静类型语言代码的执行上下文中,也可能应用到蕴含类型查看指令在内的动态确定类型指令序列,以示意动静类型相干语义。

三、ArkCompiler运行时
ArkCompiler运行时,如图6所示,被分为了外围运行时(Core Runtime)和各自语言独立的运行时插件(Runtime Plugin)。

外围运行时次要由运行时的公共外围组件形成,蕴含定义字节码格局和行为的Public ISA模块,对接零碎调用的ArkCompiler Base Platform模块, 反对Debugger、Profiler等工具的Common Tool模块和承载字节码文件解决的ArkCompiler File模块等。也提供了可选的语言无关的解释器、内存治理、编译器和并发等基础设施组件。

各语言运行时插件则蕴含各语言特有的个性实现以及规范库来撑持语言的运行行为合乎对应的语言标准,由各语言按需定制。

                                   图6 运行时框架
  1. 执行引擎

ArkCompiler运行时执行引擎有多种组件,包含解释器、JIT编译器和AOT编译器,如图7所示。

                                  图7 执行引擎构造

(1)解释器

解释器可间接运行前端编译器输入的字节码。

(2)JIT Compiler

JIT编译器个别须要运行时执行代码一段时间,Profiler生成了profiling数据之后,依据profiling数据即时编译生成高质量的机器码(上图Optimized Code II)来运行。(JIT能够依据代码执行状况实时编译生成最优机器指令)

(3)AOT Compiler

AOT编译器则是在运行前依据动态信息间接编译生成高质量的指标机器码(上图Optimized Code I)在设施上运行,PGO(Profile Guided Optimization)配置文件能够作为AOT Compiler的输出之一,给AOT Compiler一些批示,比方编译的范畴以及编译某个办法时应用哪些优化技术。通常这种PGO配置文件由在等同规格的设施上通过运行时profiling或者大数据分析生成。

无论是JIT 编译器生成的优化代码,还是AOT编译器生成的优化代码,通常都是在肯定优化假如或者优化推断的前提下生成的。如果这个前提在运行时不成立,则须要进行Deopt(逆优化),回退到解释器执行,这种状况个别较少产生。

  1. 定制化需要

各个执行引擎的性能如图8所示:

                               图8 各执行引擎的性能比照

ArkCompiler运行时通过不同执行模式的按需组合,反对多种设施不同的定制化需要。

在低端IOT设施上,ArkCompiler执行引擎反对纯解释器的执行模式,以满足小设施的内存限度条件;
在高端设施上,ArkCompiler执行引擎反对解释器配合AOT编译器以及JIT编译器的模式运行,对相当局部代码应用AOT编译器编译,使得程序一开始就能够运行在高质量的优化代码上,取得最好的执行性能;
在其它设施上,则依据设施的硬件条件限度来抉择策略,设定高频应用须要AOT编译的代码范畴,其它代码则依附解释器配合JIT Compiler运行,使得利用执行性能可能失去最大化。
为了晋升解释执行性能,在特定的体系架构下,解释器约定了将解释执行上下文中某些频繁应用的数据放在对应的物理寄存器中,比方在Arm64架构下,上下文中以后字节码指令地址、累加器值、解释器栈帧、指令映射表、以后线程对象等,间接放在固定的寄存器上,防止了在栈上频繁的加载和写入操作。

  1. 并发

简单挪动利用的开发和运行对并发有较强的需要。ArkCompiler运行时除了提供规范的“Java多线程编程”和“运行反对”之外,也提供响应式的Actor并发编程模型反对。此模型下执行体之间不共享任何数据,通过音讯机制进行通信。以后,业界的一些Actor并发模型,例如传统JS引擎的web-worker实现,有启动速度慢、内存占用低等缺点。

为了利用设备的多核能力取得更好的性能晋升,在Actor内存隔离模型的根底上,ArkCompiler运行时通过共享Actor实例中的不可变或者不易变的对象、内建代码块、办法字节码等,晋升Actor的启动性能和节俭内存开销,达到实现轻量级Actor并发模型的指标。

                                 图9 轻量级Actor实现
  1. 跨语言优化

HarmonyOS利用在某些状况下实际上是由多种语言的代码组成的。例如对HarmonyOS JS/TS利用,有一些零碎库、框架和利用依赖的局部能力的实现应用了C/C++和Java语言。HarmonyOS开发框架也提供了JS/TS与C/C++交互的JS NAPI以及JS/TS与Java交互的Channel机制。思考不同语言之间的交互场景的开发和运行效率需要,ArkCompiler和开发框架联结设计,提供了对应的优化机制。

(1)JS/TS与C/C++交互

在TS 版本的操作系统平台API实现中,通常须要面临C/C++代码拜访和操作TS对象的场景。对这个业务场景,ArkCompiler能够依据TS源码的class申明和运行时约定,生成蕴含TS对象布局形容的C/C++头文件,以及操作这些TS对象的C/C++实现库。在C/C++代码中,通过蕴含TS对象形容头文件以及链接对应实现库,实现间接操作TS对象的成果。须要阐明的是,因为TS类型或其内在布局并非总是固定不变的,因而在TS对象操作的代码实现中,会插入类型查看,如果对象类型或布局在运行时发生变化,则回退执行通用的慢速门路。

                                    图10 跨语言交互

(2)JS/TS与Java交互

HarmonyOS中有一些利用所需的能力是通过零碎、框架或利用的Java库提供的。因而在HarmonyOS利用中,也存在较多JS/TS代码与Java代码交互的场景。常见的案例中,因为JS/TS代码和Java代码有各自独立的运行环境,相互之间对于对方的数据表示、调用约定都是不可知的,所以JS/TS与Java的数据交互通常须要通过规范的JSON序列化和反序列化流程,以及经由Native层桥接的互相调用。这造成在一些场景中开销较大,影响用户体验。

ArkCompiler利用同时反对多语言的劣势,运行时具备不同语言的数据表示、对象布局、函数调用约定等信息,这使得跨语言之间的间接数据拜访、对象操作和办法调用成为可能,同时Java代码提供的更多确定的类型信息也成为JS/TS类型推导的额定输出,利于对JS/TS的编译优化。另一方面,这也使咱们能为开发者提供一个更简化的多语言编程模型,缩小须要额定手工编写的业务无关的跨语言交互代码工作量。

                                图11 简化的多语言编程模型

四、总结
HarmonyOS所反对的IoT时代下,联合利用生态、开发体验和用户体验等方面的需要, ArkCompiler与硬件、操作系统、开发框架、编程语言协同设计,在多语言对立编译运行和多设施反对的根底上,实现对HarmonyOS利用在开发和运行效率等方面的晋升。

将来,ArkCompiler在继续优化根底体验的同时,更会进一步联合HarmonyOS万物互联的需要,在跨端迁徙、多端协同等翻新场景,从编译器和运行时等方面提供底层的解决方案和优化机制,晋升分布式应用的开发和运行体验。