用XIR改善编译运行分离英语毕业论文翻译及原文.doc

上传人:laozhun 文档编号:3988523 上传时间:2023-03-30 格式:DOC 页数:38 大小:1.06MB
返回 下载 相关 举报
用XIR改善编译运行分离英语毕业论文翻译及原文.doc_第1页
第1页 / 共38页
用XIR改善编译运行分离英语毕业论文翻译及原文.doc_第2页
第2页 / 共38页
用XIR改善编译运行分离英语毕业论文翻译及原文.doc_第3页
第3页 / 共38页
用XIR改善编译运行分离英语毕业论文翻译及原文.doc_第4页
第4页 / 共38页
用XIR改善编译运行分离英语毕业论文翻译及原文.doc_第5页
第5页 / 共38页
点击查看更多>>
资源描述

《用XIR改善编译运行分离英语毕业论文翻译及原文.doc》由会员分享,可在线阅读,更多相关《用XIR改善编译运行分离英语毕业论文翻译及原文.doc(38页珍藏版)》请在三一办公上搜索。

1、本科生毕业设计英文翻译及原文Improving Compiler-Runtime Separation with XIR用XIR改善编译-运行分离Ben L. Titzer加拿大景山区公园路圆形剧场1600号谷歌94043 (650) 919-4897ben.titzerThomas Wrthinger奥地利林兹约翰内斯开普勒大学694040+43 732-2468-7137wuerthingerssw.jku.atDoug Simon加拿大门洛公园16环路太阳为系统公司实验室94025 (650) 568-4871doug.simonMarcelo Cintra加拿大尔湾市448B ICS楼

2、加州大学欧文分校安全&软件实验室92717 (949) 824-9104mcintrauci.edu摘要现阶段对虚拟机的研究迫切需要一个灵活的软件架构,来使新的设计和技术实现得到快速评估。编译器和运行系统之间的接口不仅是影响两个组件灵活性的主要因素,而且对于快速实现新优化和新特性也是至关重要的。虽然虚拟机对其内的很多组件已实现了模块化,但是在编译和运行组件如对象模型和内存管理系统之间仍存在明显的依赖性。本文通过用XIR语言精心设计一个严格的编译-运行接口来解决这个难题。编译后台是通过使用硬链接逻辑来减少机器对象操作,而XIR语言使运行系统实现了此逻辑,同时简化了后台,也使后台和运行系统彻底分离

3、。在本文中,我们将介绍编译-运行接口的设计和实现,同时也将介绍C1X动态编译器的XIR语言,其中C1X动态编译器是从HotSpotTM客户端编译器移植而来的。我们的实验结果显示,使用XIR语言虽然在编译运行时仍生成一些无关紧要的代码,对编译时间产生轻微的影响,但是它明显降低了后台的复杂性和编译-运行接口的复杂性。分类和主题描述D.3.4 编程语言:处理器代码生成;编译器;优化;运行环境。概括术语设计,实验,语言,性能关键字编译器,JIT,Java,虚拟机,降级,软件架构,对象模型,虚拟机接口,中间表示,寄存器分配,运行系统1. 简介管理虚拟机的运行软件的数量和重要性都在持续上升。新语言特性和对

4、运行软件更高性能的需求仍旧推动着虚拟机的研究和开发。快速探索新的设计方法和实现技术必须要有一个灵活的软件架构,而影响虚拟机灵活性的主要因素之一就是编译器和运行系统之间的接口,其中相关的运行系统包括垃圾回收、对象模型和同步机制等。而要在不牺牲高性能的前提下实现编译和运行的彻底分离仍然是让人头疼的难题。即使今天,对于工业强度虚拟机,当其运行系统有显著的更改时其编译器也必须做出相应的更改。编译和执行的相关性来自于以下几种软件模块。i) 平台配置。虚拟机必须给编译器配置相关的目标体系结构信息,如指令集、字长、相对字长(当使用压缩文件时它可能与字长不同)、缓存队列、堆栈队列、支持ISA扩展、可分配寄存器

5、、调用规则等。ii) 执行时访问的数据结构。编译器需要访问一些运行系统的内部数据结构,其中包括一些具有代表性的方法及它们的代码、类和常量池等。编译器也可能查询执行时解析状态的类型、字段和引用字节码的方法。iii) 优化选择和调整。执行时运行系统可能想要对每一个方法或编译标准有选择地启用不同的优化或者改变优化。尤其是运行系统希望通过动态解析信息来改变代码嵌入的方式14 1519。iv) 理论相关性。虚拟机可能乐观地假设一个非final类是叶子类来实现实体化和代码嵌入,这需要找到返回运行系统的相关性,以方便之后编译代码的优化1819。v) 安装已编译的代码。编译器生成机器码和元数据,如参考图和优化

6、信息,运行系统在执行时必须将机器码和元数据加载到它的代码区和内部数据结构中。vi) 对象模型的实现。对象操作的降级对运行系统存在很大的依赖性,同时它也包含了一大部分编译器后台逻辑。如字段访问、虚拟调用、同步操作或对诸如指针算法和内存访问之类的机器操作的内存分配等这些都是对象操作的降级。虽然许多虚拟机已经有足够的接口来实现平台配置,访问执行时的数据结构和理论相关性的定位,但是我们相信它们都没有充分实现每一个软件模块。尤其是虚拟机依赖的对象模型实现是最难实现的软件模块。本文实现了这些软件模块(除 iii)优化控制)。我们双向的编译-执行接口使编译器和运行时的数据结构抽象化,同时XIR语言允许运行系

7、统为对象操作的降级提供相应的逻辑。这样就产生了下面的设计(表示数据流的边上标注了其依赖的相关软件模块):在本文中我们将介绍C1X动态优化编译器和相应的虚拟机运行系统的分离技术。我们将重点介绍编译-执行接口。该接口使各个组件和它们的内部实现细节分离,允许编译器在编译时查询运行的数据结构,同时生成编译代码的独立于运行系统的中间表示。本文的主要贡献是一种特定领域的语言XIR语言的设计和实现,该语言允许运行系统指定对象操作实现,并且使编译器和对象模型、垃圾回收等细节彻底分离。本文的内容组织如下:第2部分介绍C1X编译器的设计概要和讨论XIR的贡献的背景。C1X编译器是从java的HotSpot客户端编

8、译器移植而来的,并且类似于HotSpot客户端编译。第3部分介绍怎样通过接口使编译和执行分离,该接口怎样表示运行时的数据结构和编译时的数据结构。第4部分介绍XIR语言和它如何使编译器与虚拟机实施细则更深层的分离。第5部分讨论结果并给出指标,包括软件复杂性、编译时间和在一套标准的基准测试程序数值上的运行性能。第6部分讨论相关产品。第7部分给出结论,并致谢。2. 编译器的设计该部分描述了C1X编译器的起源和设计,为下文对编译-执行接口的讨论和对XIR的介绍做了伏笔。2.1 C1X成因Java虚拟机想要获得工业强度的性能就必须有一个良好的动态优化编译器,使运行时间与代码质量相对平衡。当Maxine虚

9、拟机有一个新的动态优化编译器时,我们开始研究HotSpot虚拟机的编译器。HotSpot虚拟机实际包括两个编译器,客户端18和服务器端19,也分别被称为C1和C2。两者都是用C+实现的,将字节码转换成优化的机器码供解释器解析。C1编译速度快并且占用内存小,是典型的客户端默认设置编译器。而C2优化彻底并且能达到最大峰值性能,是服务器端默认设置编译器。C1设计简单,因此被移植作为Maxine虚拟机的编译器,C1忽略诸如代码移动之类的复杂优化而专于“双赢”优化进而获得高优化速度,C1执行代码嵌入、局部优化、部分全局优化,同时C1中理论的叶子类和叶子方法假定支持逆优化,并且C1中有一个良好的快速线性扫

10、描寄存器分配器。我们从OpenJDK7中提取C1的58000行C+代码,用java重写这些代码来设计C1X编译器。图1.示例代码和其产生的HIR图。实线代表控制流的边缘而虚线代表数据流的边缘。菱形代表方法的输入参数。2.2前端C1X将java字节码解析成它主要的HIR图,其中包括控制流图和值依赖图。参见图一实例和参考文献18有更多详细介绍。与值依赖图或节点表示图不同,C1X的HIR图用基本块来表示控制流,每个基本块包含一个HIR指令集的有序列表。每个HIR指令直接引用产生输入值的指令。Phi节点与基本块的起点相连。基本块连接节点,并在必要时合并数据流。SSA性质的这种表示方式是在解析字节码时以

11、CFG反序处理基本块并循环使用phi守恒估计而精心设计的。值依赖图的特性允许C1X在解析时执行大量的优化,其中包括强度降低、常量传递,常量合并、本地值编号、加载取消、无用代码删除。代码嵌入将在以下情况下发生,解析字节码时,作为参数传递给内联方法的值向前传递时。当字节码被解析成HIR图时允许内联方法使用那些优化。基于类层次分析法10的猜测性优化允许那些调用点被反虚拟化和内联化,因为反优化对已编译代码无效。随后编译通道将删除不必要的Phi,合并块,删除空格,用条件语句代替控制流,执行全局的值编号并删除无用代码。C1x和C1在优化实现上存在轻微的差别,更详细的说明,请参见文献18。2.3后端优化后,

12、C1X将高级中间表示HIR转变成低级的IR(LIR),之后执行寄存器分配和代码生成。LIR包括一个有序的基本块队列,每一个都带有有序的LIR指令队列。LIR类似于一个乘法表示,其中每一条指令有一个操作码、一个输出操作数和多个输入操作数。指令的操作数可以是物理寄存器中的操作数、虚拟寄存器中的操作数或常量。LIR指令是典型的机器级操作,如指针的加载和存储、运算和移动,但一些复杂的操作可能会生成多个机器指令并可能使用内部临时变量。与乘法不同的是,每一个LIR指令可能有无数的输入参数、无数的临时变量但只有一个输出参数。LIR不是SSA形式,它要求通过插入移动指令来移除phi节点。低级中间表示LIR与机

13、器无关,并且HIR中的大多数对象操作能被转换成与机器无关但依赖于运行的装载、存储、比较和分支。然而,一些有结构约束的操作要以依赖于机器的方式被降级(如X86中的移位和除法运算),并且那些操作可能需要物理寄存器预分配。这个转换逻辑自动声称只能生成HotSpot代码,所以采用C1手写方式实现。这个转换逻辑的实现被分成两部分:独立于机器的部分和依赖于机器的部分。我们在创建C1X和修改它来生成Maxine虚拟机代码时移植该逻辑。这种硬链接转换过程描述了本文发表的主题:降级阶段是任何编译器的经典部分,也是执行依赖和机器依赖汇聚处。虽然C1X脱离了运行时的数据结构,该数据结构在编译时需要查找(在上文提到)

14、,但是它必须为每个对象操作生成代码,而这些对象操作在执行时需要使用元数据。修改运行系统的实现,如改变调用接口,需要修改编译器的后台。在移植C1时,由于对象操作的业务不同,我们被迫对C1X降级阶段做了大量的改变。虽然java的基本操作和控制流可以独立地编译成对象模型,但我们发现那些对象操作彼此都不同(有一些例外,如读实例字段)。此外,在处理特殊情况时,如访问未定义字段或方法和慢路径操作如监听器,大量的复杂性由此而生。Maxine虚拟机和HotSpot虚拟机中的对象操作几乎完全不相同,这导致我们的第一种解决方法仅能实现慢路径操作,该解决方法是取消调用运行系统14的操作。最终Maxine后端的一系列

15、变化导致需要一个更优的解决方案:XIR,我们在第4部分讨论。3. 编译运行接口 编译器和运行系统之间的复杂相互性导致编译需要一个双向接口,这样一方为另一方提供一个接口。我们的设计使两个接口组件明确分离,编译时需要运行系统提供运行对象,运行时需要编译器提供编译对象。我们也使用了一个命名规则,即前缀Ci代表编译器提供的一个接口对象和前缀Ri代表运行系统提供的一个接口对象。首先,运行系统负责创建和配置编译器,包括选择目标体系结构和配置特定的运行设置如分配寄存器和堆栈框架整合。为了实现这一功能,编译器提供了一些具体的Ci类,这些类在执行时创建,并传给编译器完成其内部配置。我们考虑过一个更复杂的配置接口

16、,它可以避免暴露具体的Ci类,但最后我们发现这种复杂性是不合理的。其次,当运行系统请求编译操作时,它必须提供编译方法的一种中间表示,以及编译器将用的相关的数据结构。为实现这一功能,运行时必须提供RiType接口、RiField接口、RiMethod接口和RiConstantPool接口的实现,每个接口支持编译器在编译时使用的查找操作。java接口为运行系统提供了最大的灵活性;它可以通过接口实现来展示它已定义的数据结构也可以用适配器来隐藏已定义的数据结构。第三,在编译时编译器可能向运行系统的请求其他的信息,如特定的调用准则或内联决议。为了达到这个目的,运行系统必须提供一个RiRuntime接口来

17、回应编译器的请求。最后,也是最重要的,编译器将产生附加元数据的编译代码。为了这个目的,它提供了一个具体的数据结构CiTargetMethod,这样运行系统在安装和运行时只需对该数据结构回收再处理生成其内部数据结构。因为在所有情况下,编译器实现细节对用户都是透明的,并且也没必要控制那些类的生成,所以与大多数其他的Ci类类似,我们用编译器接口来提供具体的类。已选的运行接口类:l RiConstantPool:一个与字节码有关的常量池,编译器用它来查找并解析字段、类型和方法。l RiExceptionHandler:一个异常处理入口,包括字节码覆盖范围、处理索引、捕获异常的类型。l RiField:

18、:解析或未解析的字节码相关字段,包括名称、类型和封闭类。已解析的字段也允许查找属性标志如final、private、volatile等。l RiMethod:解析或未解析相关字节码的方法,包括名称、标签和封闭类。已解析的方法可能涉及方法选择或具体方法的实现,同时也允许查询属性标志如final, private, static, synchronized等。l RiRuntime:是提供给编译器各种运行服务的接口。这些服务包括Java类对象转为RiType类对象,查询系统的RiType类,调用特定方法的条件,允许嵌入的方法的协议,各种类型的查找。l RiSignature: 一个方法的标签,包括

19、参数类型和返回类型。l RiType:字节码中或其他运行接口对象中解析或未解析的java类类型、接口类型、数组类型。已解析的类型可能涉及任意的有效的java类型、响应查找的属性标志,超类型,类型是数组还是接口等。已解析的实例化类型还可以查到实现RiMethod选择器的方法。l RiXirGenerator:生成XIR的运行类。在对象操作降为机器操作时编译器调用该类。(第4部分)已选的编译器接口类:l CiArchitecture:一个表示含指令集的机器架构的对象。在创建和配置编译器时运行系统选定该对象。l CiBailout:已终止编译的异常,在输入字节码越界时或出现未预料的内部编译错误时将调

20、用该类。l CiCodePos:一个代码设置和内联调用链。该链由编译器创建,用于编译代码中的元数据。 l CiCompiler:一个能从RiMethod 实例生成CiTargetMethod 实例的对象。由运行编译方法创建、配置和使用。l CiConstant:原语表示或对象常量表示。运行阶段和编译阶段都会创建该类,用来表示或替换程序中的常量值。l CiDebugInfo:用于堆栈跟踪和优化的调试信息。它由编译器生成,包含一部分编译结果。 l CiKind:一种枚举类型,包括java基本类型、对象类型、关键字类型。涉及java虚拟机级类型时编译阶段和运行阶段都会用到该类型。 l CiLocat

21、ion:有关物理寄存器或堆栈存储单元的结构体。由编译器和运行系统创建,用来描述参数和临时变量在一个方法中的位置。 l CiRegister:物理寄存器。当涉及特定的机器寄存器时,由编译器创建,供编译器和运行系统使用。 l CiResult:编译结果,该结果中包含帮助信息和带有统计信息的目标程序。它由编译器创建并作为编译器的编译结果。l CiStatistics:编译阶段的常规统计信息,包括已编译的字节码,行数等。由编译器创建并作为编译结果的一部分。 l CiTarget:设置集。如设置引用的大小、设置缓存方式、设置可分配寄存器等。由虚拟机创建,用于配置编译器。l CiTargetMethod:

22、一个已编译的方法,包括机器码和元数据,如重定向,修改信息和调试信息。由编译器创建,作为编译结果的一部分。 l CiXirAssembler:一个面向汇编的接口,由编译器传给运行系统用来建XIR 代码。在第4节中有更详细解释。总而言之,Ri接口类由大约1300行java源代码和文档组成。而Ci类由大约2300行代码组成。在Maxine虚拟机中必要的Ri接口实现总共包括大约3500行java源代码,其中包括 RiType, RiField,RiConstantPool 和RiMethod的实现,这些接口类包装了运行系统中现有的数据结构,因此在使用时不修改这些接口来匹配Ri接口。4. XIR虽然编译

23、-运行接口的数据结构使各个组件和各自的实现细节分离,但不管怎样编译器必须生成有效机器码来实现与运行系统设计相一致的对象操作。正如第2部分所述,从对象操作到机器操作的降级对运行系统有较高依赖性。从HotSpot中移植C1X的经验使我们想出一个这样的设计,将所有降级逻辑存在运行系统中,编译器不用考虑与性系统中对象操作的实现。C1X提供给运行系统一个接口,生成XIR代码,这样的设计旨在将少对象操作。其中XIR是一种特定领域的小语言。XIR与RISC指令集的汇编语言相似。然而与RISC指令集不同的是,XIR既没有二进制格式也没有文本格式,因此将它看作是中间表示最恰当。它是一种低层次的、三地址的中间表示

24、,它拥有无数的虚拟寄存器和一套独立于机器的机器指令,如32位和64位的整数运算、指针装载和存储、条件分支但XIR语言中没有跳转计数。XIR也支持定义快捷方式和定义具体路径(参考图2),这种支持功能对字节码的实现很有用。当出错时需要进行安全检查,因此XIR中要有处理故障的代码。在方法结束生成慢路径时,编译器总会生成相应的快捷方式,这样可以充分利用指令缓存机制。此外,运行系统可以定义一个存根:可以从XIR特定的指令中调用的一块全局XIR。存根对复杂的共享逻辑有用,这种逻辑太大而不适于内联。XIR有两个调用指令变种:CALL_STUB,调用先前定义的存根,和CALL_RUNTIME,调用运行系统中任

25、意方法。图2:可以用XIR定义不同的快慢路径。快路径产生于内联,可能分支成慢路径(在方法结束时生成)。全局存根中有共享的代码,既可以从编译的方法中调用,也可以从运行系统调用。4.1两阶段除了将编译器和运行系统实现细则分离外,XIR也将运行系统和编译器的中间表示细节分离。XIR像汇编语言一样专门为运行设备而设计的。读者只需要了解这种语言是怎样描述对象操作,而没有必要去掌握SSA形式或去明确数据流边界。但这种设计导致XIR语言与编译器实际的中间表示不匹配,尤其是涉及C1X的HIR中间表示和LIR中间表示。为了避免这种问题,我们将运行系统创建XIR和编译器使用XIR分成两个不同的阶段。在第一阶段,当

26、运行系统实例化和配置编译器时,运行系统也创建一个预建XIR模板集以备后用。一个XIR模板是一个已完成的XIR代码块,该块有XirParameters已知的绑定输入参数。模板可以小到只有一个单独的XIR指令,也可以大到跟一个任意的控制流图的XIR指令那么大。在第二阶段,编译时,编译器向运行系统请求XIR;运行系统响应传回一个XIR模板和相应的输入,合称为XIR片段。图3.CiXir汇编器接口的方法。包括创建输入参数、临时变量、常量、标签、添加指令、分支、调用、完成模板。为了简便起见,用XV代替XirVariable,用XT代替XirTemplate,用XL代替XirLabel,用CK代替CiKi

27、nd,用CL代替CiLocation。这种阶段分离的设计有两个重要的好处。首先,它允许编译器在任何的编译发生之前,预处理将被运行系统使用的XIR。在此处理中,它可能将XIR翻译成内部的SSA或者数据流表示以备后用(如,将它集成到一个HIR图中)或者它可能聚集寄存器分配约束(如生成LIR需要特定的寄存器针对特定的框架)。其次,当降低每个对象操作来提高编译速度时,允许运行系统重复使用预建模板。4.2 CiXir汇编器接口运行系统在第一阶段创建XIR模板,但XIR既没有文本格式也没有二进制格式。XIR存储在编译器的内部,它是以一个由XIR指令和变量等组成的数据结构表的形式存在的。相反,运行系统用编译

28、器的汇编对象来创建XIR。图3给出了一系列用于CiXir汇编器接口的方法。汇编对象有创建XIR变量和标签的方法也有一个个添加XIR指令到一个内部有序的指令队列中的方法。在顺序实现运行系统时,调用汇编对象的实现与java语法的嵌入式语言非常类似。图4给出了实现putfield 操作的两种不同实例:未压缩参考标准和压缩参考标准。与LIL14不同的是,该汇编对象接口去掉XIR语法和XIR语法分析器,这样可以减轻编译器实现的负担,而LIL在构建编译器时需要一个LIL语法分析器。在构建CirXir汇编器时,大约900行的java源代码是用于实现CirXir汇编器的内部数据结构和接口元素Xir模板、Xir

29、片段、Xir参数。图4显示了CiXir汇编器用法的示例。它建立了两个版本的putfield操作:正常的对象引用和压缩的对象引用。两种版本的模板都将对象和字段的值作为输入,都有一个固定的输入即字段偏移。压缩版本体现了固定寄存器的用法;它假定堆底储存的是AMD64寄存器R13。帮助方法genWriteBarrier()添加XIR代码到写屏蔽的模板中(未显示)。4.3 RiXirGenerator接口第二阶段是在编译器将对象操作将为机器操作时发生。请注意,XIR没有设计用于Java所有业务的可扩展机制。我们将Java操作分为两类:独立于运行系统的操作,如初始化算法、控制流,和依赖于运行系统的操作。其

30、中,独立于运行系统的操作必须得到编译器的充分处理,而依赖于运行系统的操作必须由运行系统提供XIR语言。这样的设计减轻了运行系统的负担,同时减小了图5列出的XIR扩展点。图5。RiXirGenerator接口方法。每个方法都有一些Xir参数对象,这些对象代表编译器IR变量或节点和操作数,例如访问的字段。运行时必须向C1X编译器的后台提供一个RiXirGenerator的实现。每个方法返回一个Xir片段,该片段包括一个XirTemplate和一个XirTemplate的输入绑定。为了简便起见,用XA代替XirArgument ,用XS代替XirSnippet。编译器需要运行系统提供RiXirGen

31、erator接口的实现,该接口有为每一个HIR操作生成了相应的XIR方法。RiXirGenerator接口的每一个方法对应于一个Java操作,有两种类型的参数:自变量和操作数。自变量是不透明的XirArgument参数实例,由编译器传递,代表编译器的变量和节点,如字段访问的接受对象、写入数组的值。操作数代表一个操作的固定部分,如getfild操作的字段或者checkcast操作的类型。从字节码的角度看,自变量表示Java堆栈中的值,而操作数表示指令流的数量,如字段引用。注意图5,大部分操作数是Ri类,这自然允许运行系统使用它自己的数据结构来决定要将哪个XIR返回到编译器中。每一个RiXirGe

32、nerator接口的gen()方法返回一个Xir片段,这仅仅是一个XirTemplate,它的每一个参数绑定一个输入Xir参数,该参数被传到gen()方法中或一个Xir参数代表一个常量(例如字段偏移量常量)。回想一下,在上一阶段Xir模板在运行系统配置编译器时被创建。两阶段的方法通过从配置阶段重复使用来节省编译时间。图6显示了MaxineRiXirGenerator的一个示例,实现了XirSnippet的putfield 操作阶段。Maxine虚拟机中RiXirGenerator实现有1350行java源代码,包括注释、空白行、构建XIR模板的代码、存储的数据结构、针对不同的情况要查找的模板和

33、运行时从XIR的模板和存根中调用的实现。它包含大约240个调用的CiXirAssembler接口。图6显示了RiXirGenerator对putfield操作的实现的示例。genPutField()方法被传给接收对象,RiField,和Xir参数值。这个方法仅查找用于字段类型的模板,检查字段是否解析和返回解析或未解析的片段(guardFor()方法在需要时创建一个分解的对象,这里没有显示)。4.4编译XIR我们想尽可能为编译器的实现保留更多的设计自由。不是接口能做什么而是接口不能做什么。例如,RiXirGenerator 不允许运行系统在编译时对降级操作做任何假设。其次,编译器传递的Xir参数

34、句柄隐藏了编译器的IR实现细节。此外,运行系统不能假定操作以任意的特定顺序降级(无论是根据它们出现在方法或被编译的内联方法的顺序或任何其他顺序),运行时必须独立考虑每一个操作。运行系统还不能假设所有的操作同时降级;编译器可能降低一些操作,执行优化,提出运行界面的其他问题,降低更多的操作,执行更多的优化等。从某种意义上来说,编译器希望RiXirGenerator是通用的。这样的设计使编译器可以任意排列其他阶段的降级。虽然C1在从HIR转变成LIR时执行了所有的降级,但其他的编译器可能通过用机器级的节点代替对象操作节点来执行降级,但保持相同的IR框架,允许相同的优化在降级前或降级后执行。如果对象操

35、作成为几个机器操作,作为消除共同的子表达式或代码方案的候选,降级后的优化将是非常重要的。例如,在运行系统通过XIR生成装载操作后,对一个对象的元对象重复访问,如invokevirtual,checkcast或instanceof的操作,可能重复使用元对象加载。此外,写屏蔽和同步机制可能生成算术表达式和访问本地线程值,该值是CSE和代码运行很好的候选者。然而,一个不同的编译器或相同的编译器在一个较低优化水平不可能在降级后执行很多优化,并且在降级时可能(像C1和C1x)从HIR降为LIR。在我们的设计中所有选择都是允许的。C1X将手写的降级逻辑的回归套件传给Maxine虚拟机之后,我们选择在C1X

36、后台编译XIR,。这样做除了有一个稳定的工作平台外,它还允许我们评估执行效率,同时也允许我们将XIR生成的逻辑和传统的手写逻辑进行比较。正如第2节提到的,C1X的LIR的设计允许每个LIR指令有任意的输入、任意多的临时变量但只能有一个输出。线性扫描寄存器分配23已经处理了这样的指令,所以它可以轻松地添加一个特殊的XIR指令,该指令表示一个完整的XIR片段即带有输入、临时变量和输出。然后,我们修改了HIR转换LIR的方式,这样就可以从RiXirGenerator请求XIR或者执行选定的上级逻辑;那意味着两种机制在相同的编译器是全能的。那意味着两种机制在相同的编译器是全功能性的。如果XIR选项被启

37、用,后台将生成一个单一的LIR指令,表示XirSnippet 从运行系统的RiXirGenerator返回。将生成XIR的过程分成两个阶段的方法对我们的实现是非常有用的。在第一阶段,当运行系统构建XirTemplates时,C1X预处理每个模板,收集任何构件注册约束(如,如果它执行某一部分,就需要在X86做某些注册),并确定模板是否有一个慢路径或其他调用。预处理的记录信息附在XirTemplate以备后用。在降级时它被转换成LIR指令,并由寄存器分配器用于给LIR指令的输入、临时变量、输出分配寄存器。请注意因为整个XirSnippet是用一个单独的LIR指令表示,即使它包含内部控制流,寄存器分

38、配器假定一个片段的所有模板和输入在XirSnippet同时存在。另外,在执行寄存器分配之前,LIR指令一从运行系统返回时,就生成XIRSnippet的LIR指令。这样寄存器分配器就可以计算一个片段中临时变量的生命周期,也可以用于其他的变量并作出好的决策,但在处理大量的变量和指令时耗费大。在执行寄存器分配之后,C1X执行一些简单的优化,如删除无用的活动和跳转,也可能重新排列短循环的基本块。然后C1X访问LIR指令的基本块并由LIR指令生成LIR的机器指令。为了支持XIR,我们只增加支持编译的特殊LIR指令,该指令用XirSnippets表示。它是这样来实现的:通过开放XirTemplate然后从

39、快路径访问XIR指令,一个一个生成相应的机器码。如果XirTemplate有一个慢路径,那么慢路径将在方法结束时被添加。在C1X后台实现XIR总共需要600行代码。图7a给出了每个基准在两种优化级别的静态编译测试。级别-O1包括本地优化和简单的控制流优化,而级别-O3能进行所有的优化。各栏标注:方法的数目、字节码大小、无XIR的机器码大小、有XIR的机器码大小相对变化的百分比,无XIR时以秒计的编译时间,有XIR编译时间相对变化的百分比。图7b(紧接7a的列)是对编译IR的统计,包括HIR指令数、LIR指令数、无XIR的LIR相对变化的百分比、XIR指令比例。5. 实验结果在该部分,我们给出了

40、C1X有XIR和无XIR两种情况比较的实验结果。我们介绍了几个相应代码库静态编译指标,其中包括编译方法的种类、字节码的长度和HIR指令数。然后我们比较了有XIR和无XIR两种类型的静态编译指标,其中包括编译速度、LIR指令的数量、XIR执行指令数和编译代码量。接着我们通过测试C1X基准的执行时间来比较有XIR和无XIR两种类型的代码质量,其中C1X基准是安装在Maxine虚拟机中,作为动态编译器的组成成分。5.1静态测试图7给出了在收集了大量有XIR和无XIR两种类型下的C1X汇编指标数据下的实验结果。该实验运行在一个英特尔Nehalem四核处理器2.66GHz,8GB内存和64位的OpenS

41、olas。我们选择将C1X作为工业虚拟机的一个用户应用系统,因为这样允许更快的开发时间,使我们获得更广泛的测量。对于这些实现我们将C1X作为HotSpot服务器虚拟机的用户应用系统,其中HotSpot服务器虚拟机具有2GB堆存储空间,其版本是1.6.0_13。为了支持静态编译脚本,玛克辛类装载机、类文件解析器、验证和内部运行的数据结构也需在用户模式下运行。在虚拟机主机“预热”运行C1X,经过5个预热迭代随后平均10个以上的迭代呈现之后,定时测试数据被收集。图7a和7b分析了一些方法序列对编译的影响。首先可以观察到几种基准的机器码在递增。与手写逻辑实现相比,更多的快路径操作用XIR和内联来实现,

42、这样很多情况下运行时调用的速度变慢但调用的模块变小了。其次,可以观察到每增加三个基准编译时间就会增加27%,这是因为后台必须做更多的工作去通知运行系统降级每个操作,而不是只简单的执行手写逻辑。然而事实上,两个基准会减少编译时间,这因为这些基准使XIR减少了很多的LIR指令,寄存器分配和LIR优化只需要很少的工作量。图8显示的是有XIR和无XIR的C1X执行时间的结果,C1X安装在Maxine虚拟机的优化编译器上。竖形条上的数据随着XIR的变化而变化。5.2动态测试图8给出了有XIR和无XIR两种情况下执行时间的比较。在本实验中,C1X将其自己编译成Maxine虚拟机的脚本,被配置成优化编译器。

43、C1X在运行热方法时由方法调用计数器来触发,其中方法调用计数器嵌在Maxine虚拟机的非优化编译器中。我们选择运行SpecJVM98基准套件和在DaCapo7套件。不幸的是,由于最近Maxine复原,我们不能报告bloat基准、lusearch基准和xalan 基准的结果。我们对每一个基准进行了5次运行测试,每次运行都是一个新的虚拟机实例。运行系统的计时是从虚拟机程序启动开始到虚拟程序结束为止;基准报告的任何内部时序数被忽略。我们是在各个基准默认的配置下运行它们的。图8显示大多数指标都受到5%以下的影响,三个局外者:jess和db都是相对地被减慢10%和24%,pmd被加速12%。大多数的Da

44、Capo基准用XIR运行的更快,但所有的SpecJVM98没有XIR却运行的更快。大多数用XIR加速,因为Maxine XIR的实现为叶子类的类型测试、接口调度和没有用手写逻辑复制的其他操作提供了特殊的实现。另一方面,在图7a的静态性能测试结果显示XIR经常会增加编译时间,在这种情况下当然也会增加编译时间。另外,我们注意到XIR有时也产生质量差的代码,因为没有对XIR模板分配寄存器,而在模板之间分配。一些性能程序的减慢可能是由于热循环效应的出现,也可能是由于差的高速缓存行为和XIR过大的机器码。5.3简单后台虽然XIR的主要目的是将编译器和降级操作逻辑分离,但将这种复杂性移到运行系统对简化编译

45、器有副作用。为了测量复杂性的减少,我们分离出C1X的源代码,删除所有的手写降级逻辑,创建测试分支。(请注意,这个分支在上节中没有去获得任何数据。)我们首先移除逻辑来将HIR对象操作转变成LIR操作,复杂的LIR指令必须也只能支持这种逻辑;从C1X中移除2000行Java代码,所有后台的代码量从53000行变为51000行。然后从接口中移除大约10个只用于该逻辑的方法;例如,一个字段的偏移量、在一个虚拟表中的索引方法、一个对象首部的长度等。这降低了编译器运行接口类的大小,从4700行减到4600行,实现Maxine的代码量也有类似的减少(从5500行减到5400行)。但比从接口中移除代码的数量更

46、重要的是诸如对象首部的大小和偏移量、从一个对象开始的偏移字段,接口的ID和虚拟方法的索引之类的内容不再出现在接口上。这样为运行系统提供了更多的自由,减少了编译器的硬链接逻辑。我们的代码删除演习是非常初级的(只有几个小时);我们期望更深入的对XIR的后台进行重设和重构来进一步降低其复杂性。6. 相关工作高级语言操作在编译或解释时有时需要先转变成机器语言操作。在虚拟机中,任意的JIT编译器,动态编译器和解释器都需要实现高级语言向机器语言的转变。Jlikes虚拟机包括两个编译器:一个基本编译器和一个优化编译器,基本编译器用来模仿java操作数栈将字节码一个接一个的迅速转换成机器码,该编译器基本上是一

47、个单向的代码生成器,在代码生成过程中包含了硬编码语义。Jlikes虚拟机的优化编译器有三个代表:HIR,LIR和MIR。HIR,高级中间表示适用于Java的基层操作人员和一些白盒测试人员;LIR,低级但独立于机器的中间表示;MIR,特定机器的中间表示。大多数的降级发生在从HIR向LIR转变,HIR指令被扩展到LIR操作,这是Jlikes虚拟机的运行系统的特性,如对象布局和规则调用。这里再次提一下,Jlikes虚拟机运行系统的特性是翻译硬编码。例如,要展开一个HIR指令就代表要调用一个虚拟方法,额外的LIR指令生成经对象TIB引用的虚拟方法的加载地址。加载地址被LIR指令调用。 将LIR转成MI

48、R中设置了写屏蔽,虚拟机和GC之间的接口存在屏蔽设置,但它不包括对象操作的降级。主要密切相关的工作是开放式运行平台的LIL语言,LIL语言是一种非常类似于XIR的语言,它描述了对象操作和其他运行服务的实现。与XIR不同的是,LIL是一种文本语言,也就是在运行是生成的C字符串,在编译时被送入由编译器实现的解析器中。虽然本文中没有提及影响性能的一种策略,但我们的汇编对象接口避免了解析字符串的开销,我们相信这样更清晰。此外,生成XIR的两阶法在编译时无需创建和验证XIR。另一个不同点是,LIL存根执行它们自己的活动框架,即使9指出它们是“内联”编译器。不清楚实际的内联机制是什么,特别是目前还不清楚是否将临时对象和输入信息等价为寄存器分配器中的LIL存根,和是否需要在特定的寄存器或堆栈单元中调用。与此相反,XIR的输入量,临时变量和输出量等同于寄存器分配器的其他变量,XIR中的“内联”一直都是第4部分所述的含义,它支持快路径,慢路径,全局变量和运行时调用。LIL也有一些特定的运行框架如访问本地线程。正如示例所显示的,任何这样的构造对XIR来说没必要,因为C1X允许运行时预

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 办公文档 > 其他范文


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号