你深入解析过java虚拟机:C1编译器从HIR到LIR吗?
2022-08-08 20:49:45
LIR犹如于三操作数的完毕,但众了极少诸如对象分派和加锁的高级指令。C1遍历HIR的每个基础块,为每个基础块的每条SSA指令天生对应的LIR指令。从HIR到LIR的转换历程由LIRGenerator杀青,如代码清单8-15所示。
pin只是一个优化手脚,假使未被pin住,只消有必要,编译器照样会为它天生对应的LIR。譬喻当照料i3时,编译器必要将i2、i3举动加法指令的操作数,此时它会行使LIRItem包装i2和i3两个操作数,并挪用walk()为它们天生对应的LIR。天生LIR的历程如代码清单8-16所示。
C1正在天生LIR时还会遭遇许众题目,有些指令,如new、monitor操作,必要与虚拟机的很众组件交互,为它们天生LIR指令是一个繁杂且坚苦的劳动,如代码清单8-17所示。
正在HIR中,正在差异基础块为统一个变量(假设是x)赋值时也许会行使差异的SSA指令,如图8-7a所示,左边基础块x的赋值被外现为n1=10,右边基础块x的赋值被外现为n2=20,最终它们的后继基础块行使phi指令归并数据,x被外现为n3=[n1,n2],如许适宜SSA的界说。但LIR不是SSA,不必要恪守它的原则,且LIR必要更进一步相识底层架构,Phi该当被打消,此时统一个变量x正在差异基础块中行使沟通的寄存器R1存储。
线性扫描寄存器分派体例会为LIR的虚拟寄存器分派一个物理寄存器,假使物理寄存器的空间不敷,则用内存替代(溢出到内存,之前寄存器的读写形成内存所在的读写)。C1行使线性扫描寄存器算法(Linear Scan Register Allocation,LSRA)满意它的计划理念,LSRA算法的整个完毕位于c1_LinearScan中,该算法始于do_linear_scan(),如代码清单8-19所示。
live_gen(天生集):正在眼前基础块中行使,正在先驱基础块界说的值。live_gen又称use集。
live_kill(杀死集):正在眼前基础块界说的值,该值也许“杀死”其正在先驱基础块的界说。live_kill又叫def集。
对付每一条LIR指令,这一步都市检验它的输入操作数、输出操作数、且自操作数。假使输入操作数没有位于live_kill集,即眼前基础块没有界说,那么将它参加live_gen集。输出操作数和且自操作数都参加live_kill集,由于它们是对值的界说。接着行使compute_global_live_sets将数据流解析扩展至通盘基础块,它的中央机思可用下面的数据流方程外现:
该数据流方程的思思源于这些简易的究竟:假使正在一个基础块行家使了一个值,那么该值肯定存正在于基础块的live_in群集;假使一个值存正在于live_in群集,那么它肯定存正在于该基础块的某个先驱基础块的live_out群集,由于统制流的边不会天生新的值;假使一个值存正在于live_out群集,并且它没有正在眼前基础块中界说,则眼前基础块的live_in集肯定包蕴它。
读者也许一经展现,遵照live_gen、live_kill获得基础块live_in和live_out集的历程是一个由下至上的历程,以是灵活解析是一个反向数据流解析。当数据流解析杀青后build_intervals首先构制存活限制,如代码清单8-20所示:
图8-8a所示为初始化R42和R43。指令38、40不影响存活限制。指令36会将R42的存活限制修削为[36,42[,同时新增R45的存活限制[24,36[。指令34将R43的存活限制修削为[34,42[,同时新增R44的存活限制[24,36[。当这一步杀青后,存活限制如图8-8b所示。随后指令32将R45的存活限制修削为[32,36[。指令30将R45的存活限制修削为[30,36[。指令28将R44的存活限制修削为[28,34[,然后为R42新增存活限制[28,30[。指令26将R44的存活限制修削为[26,34[,为R43新增存活限制[24,26[。当扫数杀青后,存活限制如图8-8c所示。深色黑条外现该值正在该处行使(use_position)。
构制存活限制的中央机思是起初用live_out集初始化存活限制,接着从基础块终末一条指令启程向上遍历,然后遵照指令输入、输出且自修削存活限制,整个完毕如代码清单8-21所示。
下篇作品给专家讲授的是深切解析java虚拟机:C2编译器,编译流程;以为作品不错的友人能够转发此文合怀小编;感动专家的支撑!