具体实施方式
以下仅通过示例的方式描述本发明的实施例。这些示例表示申请人当前所知道的将本发明付诸实践的最佳方式,尽管它们不是仅有的能够实现本发明的方式。该描述阐述了示例的功能和用于构建和操作示例的步骤的顺序。然而,可以由不同的示例来完成相同的或等同的功能和顺序。
如上所述,在处理器中存在有限数量的虚拟寄存器,并且因此在寄存器中存储的一些变量可能需要被保存到堆栈以释放虚拟寄存器。
堆栈是通常用于提供供程序使用的临时存储区域的数据结构,并且堆栈被实现为后进先出(LIFO)数据结构(其可以替选地被称作先入后出FILO数据结构),从而使得严格地以被添加到堆栈的相反次序从堆栈中移除数据。堆栈可以被认为是从堆栈原点向上或向下生长;然而,在任一种情况下,由堆栈指针来标识堆栈的顶端(其是被最新添加到堆栈的元素),该堆栈指针通常指向下一非使用的条目。堆栈指针的值可以被存储在处理器内的硬件寄存器中。当数据元素被添加(或“推入”)到堆栈时,它被写至堆栈指针所指向的位置,并且例如通过将指针增加4(即,4字节=32位)来更新堆栈指针的值(从而使得该堆栈指针指向下一非使用的条目)。当随后从堆栈中移除(或“弹出”)数据元素时,移除该数据元素并且更新堆栈指针以指向更短堆栈上的下一非使用条目。
为了以下描述的目的并如图1所示,堆栈100(具有由箭头102所指示的堆栈原点)被认为是向下生长,从而使得当数据被推入堆栈时(例如,当添加数据108时,在堆栈指针从由箭头104所标记的位置移动到由箭头106所标记的位置时)堆栈指针的值减小,并且当从堆栈弹出数据时(例如,当移除数据108时,在堆栈指针从箭头106所标记的位置移动至由箭头104所标记的位置时)该堆栈指针的值增大。然而,将理解到,如果堆栈被认为是向上生长(其中当数据被推入堆栈时堆栈指针值增加,而当数据被从堆栈弹出时堆栈指针值减小),则本文中描述的方法是同等可应用的。
在由程序所使用的变量被存储在堆栈100中的情况下(例如,在存在不足的虚拟寄存器情况下),这些变量被存储在可以由距当前堆栈指针位置(如由箭头114所指示的)的偏移112所引用的位置中(例如,图1中的阴影位置110)。替选地,当在函数中存在动态堆栈生长的情况下,则位置可以由距帧指针而非堆栈指针的偏移所引用。如果在堆栈中存储的变量随后被读取回寄存器中从而能够使用这些变量,则这引入了显著的延迟。
以下描述对堆栈保存变量的值进行预测的方法。通过预测该值,而非从存储器中读取它,消除了与从堆栈中读取该值相关联的延迟。在以下描述的方法中,当变量被存储到堆栈以释放虚拟寄存器时,连同变量被存储在堆栈中的何处的细节一起,数据条目被存储在记录了其中存储有变量的值(在它被存储到堆栈中之前)的物理寄存器的数据结构中。随后,当存在从堆栈加载变量的指令时,之前存储的数据条目能够用于标识哪个物理寄存器包含该变量的值,并且新的虚拟寄存器能够被映射到该物理寄存器。在一些示例中,不执行从存储器的加载,而在其它示例中,仍然可以执行从存储器的加载但预测的值用于使得能够与该加载并行地执行相关指令并且该加载仅用于核对预测的值(即,在数据结构中标识的、并且新的虚拟寄存器被映射到的物理寄存器中存储的值)是正确的。还描述进一步的方法,这些进一步的方法使得该方法能够在函数调用之后和/或在未使用寄存器重命名的情况下工作。
图2示出了对保存到堆栈的变量的值进行预测的示例方法的流程图。如图2所示,当出现参照堆栈指针而存储值(在更高等级的构造中,该值也可以被称作变量)的指令时(块202),与该存储指令对应的数据条目被存储在新的数据结构中(块204),该新的数据结构在本文中可以被称作“堆栈保存的值存储区”。例如可以通过检查该指令以查找在重命名前具有以下形式的指令来检测(在块202中)该指令:
ST Reg,[SP+#offset]
(其中,Reg可以是任意的,#offset(偏移)是字面值(literal value),并且SP指的是虚拟堆栈指针),并且在一些示例中,也可以使用基于PC(程序计数器)的信任表(confidence table)。每个数据条目包括三个元素:存储当前堆栈指针值的物理寄存器的寄存器ID(PSP)、在变量的值被存储到堆栈之前存储该变量的值的物理寄存器的寄存器ID(P)、以及到该变量所存储处的当前堆栈指针的偏移(O)。在一些示例中,所有三个元素(PSP、P和O)被全部包括在一个数据条目内。在其它示例中,尽管在数据条目内表示所有三个元素,但是它们可以不被全部包括;然而,这可能导致增加数量的不正确预测。例如,可以包括PSP的位的子集而非全部包括PSP,并且类似地可以包括O的位的子集而非全部包括O。在其它示例中,可以使用来自PSP和O的位的组合(而非全部的PSP和O),例如,PSP和O的“哈希”,例如,使用XOR逻辑函数来将PSP的位与O的位进行组合的结果。
如上所述,当在函数中存在动态的堆栈生长的情况下,可以通过距帧指针而非堆栈指针的偏移来参照该位置,并且在这种情况下,将存储当前帧指针值的物理寄存器的寄存器ID存储在适当的位置(PFP),而非存储当前堆栈指针值的物理寄存器的寄存器ID(PSP)。然后,所存储的偏移O是到该变量所存储处的当前帧指针的偏移。查找过程维持不变,而不管在两种情况下是PSP还是PFP被存储作为存储物理寄存器ID的表。为了清楚的目的,以下描述指的是存储PSP,但是将理解到,该描述也适用于存储PFP的情形。
在堆栈保存的值存储区中存在许多数据条目,并且该存储区可以被设置为以任意适当的方式存储实施例,并且以下描述的示例仅提供少量的示例。在一个示例中,每个数据条目可以包括用于堆栈指针的5位寄存器ID,用于寄存器ID的5位条目保持有在变量被存储到堆栈中之前的该变量并且如果容纳堆栈内的可能偏移的全部范围,则偏移可以例如是16位尺寸。然而,由于实际偏移可能在尺寸上要小得多,因而在堆栈保存的变量存储区中存储的偏移的尺寸可以在尺寸上被限制到例如5位。
在能够被存储在堆栈保存的变量存储区中的偏移的尺寸被限制(例如,到5位的条目)的情况下,存在多个不同机制,这些机制可以用于解决不太可能发生的实际偏移大于能够在可用条目中存储的偏移的事件。在第一示例中,未存储条目,从而使得随后不能预测变量的值并且相反必须从堆栈中读取变量的值。在第二示例中,仅存储底部(即,最不重要的)位。如果所存储的这5位与在堆栈保存的值存储区中的另一条目匹配,则这可能导致不正确的预测。在第三示例中,所存储的偏移值可能不是实际偏移,而是创建5位值的偏移值的哈希(这减少了堆栈保存的值存储区中的两个条目之间的匹配的风险)。
在示例中,可以识别出以下参照堆栈指针而存储值的指令(在块202中):
ST R2 8[SP]
其中,R2是要被存储到堆栈中的虚拟寄存器,并且这要被存储在距堆栈指针(SP)的偏移8处。这可以通过处理器中的解码和重命名级来(使用寄存器重命名映射)进行翻译以:
ST P8 8[P4]
假设在该示例中,虚拟寄存器R2当前被映射到物理寄存器P8并且当前堆栈指针被存储在物理寄存器P4中。这导致堆栈保持的变量存储区中的条目包括三个元素:P4、P8和8。
在示例中,堆栈保存的值存储区可以是表的形式:
其中,第一列存储与堆栈指针对应的寄存器ID(PSP),第二列存储虚拟寄存器所映射到的、在寄存器ID被写到堆栈之前的寄存器ID(P),并且第三列存储偏移值(O)。
当在堆栈保存的变量存储区中存在有限量的空间的情况下,可以移除数据条目以为新的数据条目制造空间,并且这可以例如基于逐出最近最少使用的数据条目以当堆栈保存的变量存储区变满时为新的数据条目制造空间的最近最少使用(LRU)算法或其它替代算法。附加地,或代替地,当程序从函数返回时,可以从堆栈保存的变量存储区中移除条目。在这样的示例中,当程序从函数返回时,从堆栈保存的变量存储区中清扫来自该函数的所有条目,并且对堆栈保存的变量存储区中的哪些条目应当被清扫的确定可以基于堆栈指针。这是因为来自函数的所有条目将具有同样的堆栈指针值,除非使用动态存储器分配。在使用动态存储器分配的情况下,存储PFP而不是PSP,并且因此可以使用同样的驱逐方法(这将因而清扫具有同样的PFP的所有条目)。
在一些示例中,该方法可以检查重复(duplication),即,在与堆栈保存的变量存储区中的现有条目的位置相同的位置的存储(即,相同的PSP和O值)。在出现关于与现有条目的位置相同的位置的存储的指令的情况下,堆栈保存的变量存储区中的该现有条目可以被新的条目覆写。如果该方法不检查重复,则减少实现该方法所需的逻辑量,但是代价为减少的预测正确性。
当随后出现将变量从堆栈加载到虚拟寄存器的指令时(块206),搜索堆栈保存的变量存储区以查看是否存在与加载指令对应的条目(参见块206中),即,包括距相同堆栈指针的相同偏移的条目(即,堆栈指针值所存储处的相同物理寄存器ID)。例如可以通过检查该指令以查找在重命名之前具有以下形式的指令来检测该指令(在块206中):
LD Reg,[SP+#offset]
存在能够执行搜索(在块208中)的许多不同方式,并且这将至少部分地根据堆栈保存的变量存储区的结构。以下更详细地描述各种结构及其对搜索操作的影响。
在示例中,堆栈保存的变量存储区可以包括以下条目:
其中,如上所述,第一列存储与堆栈指针对应的寄存器ID(PSP),存储虚拟寄存器所映射到的、在寄存器ID被写到堆栈之前的寄存器ID(P),并且第三列存储偏移值(O)。
如果出现以下形式的加载指令(在块206中):
LD R3[SP+#8]
其中,R3是从堆栈加载的值要被存储到的虚拟寄存器(其之前被称作“新的虚拟寄存器”以将“新的虚拟寄存器”区别于在变量被存储在堆栈中之前与该变量相关联的原始虚拟寄存器),则这可以被翻译(使用寄存器重命名映射)为:
LD P10[P4+#8]
这是由于虚拟寄存器R3当前被映射到物理寄存器P10并且堆栈指针值当前被存储在物理寄存器P4中(其也可以被描述为堆栈指针当前被映射到P4)。
因此,该搜索(在块208中)查找包括偏移8和堆栈指针P8的对应条目。在该示例中,堆栈保存的变量存储区中的第二条目匹配这些标准并且因此在堆栈保存的变量存储区中找到对应条目(块210中的“是”)。
响应于找到堆栈保存的变量存储区中的对应条目(块210中的“是”),通过将在对应条目中的标识的寄存器中当前所存储的保存的变量值(例如,上文示例中的P8)移动到(在加载指令中标识的)虚拟寄存器所映射到的物理寄存器(例如,上文示例中的P10),该方法能够继续。然而,不需要执行该移动并且代替地可以发生寄存器重命名以将在加载指令中标识的虚拟寄存器映射到在对应条目中标识的物理寄存器(块212),即,可以直接利用在对应条目中标识的物理寄存器来更新寄存器重命名映射。因此,在上文的示例中,更新寄存器重命名映射从而使得虚拟寄存器R3被映射到P8而非P10(在块212中),而不是将值从物理寄存器P8移动到物理寄存器P10。
在该示例中,从未执行过加载指令;然而,在其它示例中,仍可以执行从堆栈进行加载,如上文所描述的那样。在任一情形下,变量的值是可供另一指令所使用的(例如,相关的指令)而没有与等待将变量从堆栈加载到寄存器中相关联的延迟。
如果该搜索(在块208中)不导致在堆栈保存的变量存储区中找到对应条目(块210中的“否”),则以标准方式将该变量从堆栈加载到所标识的物理寄存器中(即,当前被映射到加载指令中标识的虚拟寄存器的那个)(块214)。
图3示出了可以实现本文中描述的方法的示例乱序处理器300的示意图。在该示例中,处理器300是单线程处理器;然而所述方法也可应用于多线程处理器(其中,每个线程将维持使用单独堆栈指针的单独堆栈)。
处理器300包括提取级302、解码和重命名级304、重新排列缓冲区306、提交级308以及一个或多个功能单元310,一个或多个功能单元310各包括一个或多个执行流水线。处理器300进一步包括寄存器重命名映射(或寄存器重命名表)312,由解码和重命名级304(或者由解码和重命名级304内的寄存器重命名模块)来维护寄存器重命名映射312。
提取级302被配置为从如由程序计数器(PC)所指示的程序中(按照程序次序)提取指令。一旦提取到指令,则该指令被提供至解码和重命名级304,解码和重命名级304被设置为解释指令并执行寄存器重命名。特别地,每个指令可以包括:寄存器写操作;一个或多个寄存器读操作;和/或运算或逻辑操作。寄存器写操作向目的地寄存器写,并且寄存器读操作从源寄存器读取。在寄存器重命名期间,利用物理寄存器来替代(或重命名)在指令中提到的每个虚拟寄存器(例如,每个源和目的地寄存器)。
对于寄存器写操作,所提到的虚拟寄存器(例如,目的地寄存器)被分配未使用的(或可用的)物理寄存器。任意分配均可以被存储在寄存器重命名映射312中,其中,寄存器重命名映射312是示出每个虚拟寄存器与被分配给程序流中的指令的物理寄存器之间的映射的数据结构。对于寄存器读操作,能够根据寄存器重命名映射312中的由虚拟寄存器所索引的条目而确定用于特定虚拟寄存器(例如,源寄存器)的正确物理寄存器。
可以在解码和重命名级304内实现上文参照图2所描述的方法,并且在示例中,解码和重命名级304可以包括执行在图2中示出的并且在上文描述的方法的堆栈保存的变量预测模块314。如上所述,该模块314(或解码和重命名级304)维护在本文中被称为作堆栈保存的变量存储区316的数据结构。
在指令通过解码和重命名级304之后,指令被插入记录缓冲区306(ROB)中并被派送到功能单元310以用于执行。指令被派送到的功能单元310可以基于指令的类型。重新排列缓冲区306是使得指令能够被乱序执行但被有序提交的缓冲区。重新排列缓冲区306按照程序顺序而保持被插入到重新排列缓冲区306中的指令,但是能够由功能单元310乱序地执行ROB 306内的指令。在一些示例中,重新排列缓冲区306能够被形成为循环缓冲区(circular buffer),循环缓冲区具有指向ROB 306中的最旧指令的头,以及指向ROB 306中的最新指令的尾。按照程序顺序将指令从重新排列缓冲区306输出到提交级308。换言之,当指令已经被执行时,从ROB306的头输出该指令,并且该头递增到ROB 306中的下一指令。从重新排列缓冲区306输出的指令被提供至提交级308,提交级308将指令的结果提交至寄存器/存储器。
每个功能单元310负责执行指令,并且可以被配置为执行指定类型的指令。例如,功能单元310可以包括加载-存储单元、整数单元(integer unit)、浮点单元(FPU)、数字信号处理(DSP)/单指令多数据(SIMD)单元或乘积累加(MAC)单元中的一个或多个。整数单元执行整数指令,FPU执行浮点指令,DSP/SIMD单元具有同时对多个数据点执行相同操作的多个处理元件,并且MAC单元计算两个数的乘积并将该乘积加到累加器。功能单元内的流水线可以具有不同的长度和/或复杂度。例如,FPU流水线通常比整数执行流水线更长,这是因为它一般执行更复杂的操作。
处理器300还可以包括图3所示的那些功能元件之外的功能元件(例如,它可以包括寄存器堆缓存(RFC)和寄存器堆(RF))。处理器可以进一步包括分支预测器,分支预测器被配置为在已知导致可能的流改变的指令(例如,分支指令)的情况下预测程序流将采取哪个方向。分支预测是有用的,这是由于在已知分支指令的结果之前分支预测使得能够由处理器300推测性地执行指令。当分支预测器准确地预测程序流之前,这改善了处理器300的性能。然而,如果分支预测器不正确地预测分支方向,则发生误预测,需要在程序能够执行之前来纠正该误预测。为了纠正误预测,放弃发送到ROB 306的推测性的指令,并且提取级302开始从正确的程序分支提取指令。
将理解到,其它处理器可以不包括图3中所示的所有功能元件(即,可以省略图3中所示的功能元件中的一个或多个),并且在一些示例中,可以包括图3中未示出的附加功能元件。
如图3中所示,单独地并且并行于维护堆栈保存的变量存储区316,由解码和重命名级304维护寄存器重命名映射312。替选地,这两者可以被组合在单个数据结构中,其中一部分(其对应于寄存器重命名映射312)由虚拟寄存器来索引,而其它部分(其对应于堆栈保存的变量存储区316)由PSP和O来索引,但是两个部分仍被独立地更新。解码和重命名级304可以包括堆栈保存的变量预测模块314,堆栈保存的变量预测模块314被设置为存储堆栈保存的变量存储区316中存储值(例如,如上文参照图2所描述的)。
尽管可以替选地可以在加载-存储单元(其是功能单元310之一)中缓存虚拟寄存器的值,但是通过如本文所述而预测解码和重命名级304中的堆栈存储的变量的值,更早地预测该值并且没有必要从缓存加载任何值(相反,预测涉及重命名操作)。
如上所述,在一些示例中,可以不执行加载指令(参见块206中)。然而,在其它示例中,仍可以执行加载指令以核对在堆栈保存的变量存储区的对应条目中标识的物理寄存器中存储的值是正确的,如图4中所示。然而,在这样的示例中,与执行其它指令(块404)并行地执行加载(块402),即其它指令不等到从堆栈加载该值。当已经从堆栈加载该值时(在块402中),所加载的值与预测出的值进行比较(块406),即,所加载的值与在堆栈保存的变量存储区中的对应条目中标识的物理寄存器中存储的值进行比较。如果值匹配(在块406中的“是”),则通过验证并且不需要倒回(rewind)。然而,如果值不匹配(在块406中的“否”),则将根据预测出的值的所有指令倒回(块408)。在一些示例中,相比于仅倒回根据预测出的值的那些指令,可以倒回更多的指令,例如,可以倒回已并行执行的所有指令。
可能出现值匹配的缺乏(导致块406中的“否”),这是因为在存储指令(参见块202中)与加载指令(参见块206中)之间的时段中已经重写了在对应指令中标识的物理寄存器,或者因为一些东西已经更新了堆栈中的变量值。
通过并行地执行加载(如图4中所示),存在所预测的值与其它线程或调试器(其可能希望更新在堆栈中存储的寄存器值)的增加的相干性(coherency)。由于在堆栈中存储的寄存器值不太可能被其它线程或调试器所改变,因而误预测率很可能非常低。
堆栈保存的变量存储区316可以使用在存储器缓存中使用的缓存技术中的任意技术,例如,从直接映射至完全关联的任意等级的关联性。在示例中,可以使用偏移位中的一个或多个作为到堆栈保存的变量存储区中的索引,这减少了需要被搜索以找到对应条目的条目数量(例如,在图2的块208中)。图5示出了堆栈保存的值存储区500的示例,其中,使用偏移的3个最低有效位(LSB)来索引存储区500中的条目。在这样的示例中,存在对应于偏移的三个LSB(二进制为000-111(十进制为0-7))的8行50-57。
当存储数据条目时(例如,在图2的块204中),条目被存储在根据偏移的三个LSB的数据条目中的一行中,然后数据条目自身包括堆栈指针的寄存器ID(PSP)、保持有被存储到堆栈的值的物理寄存器的ID(P)以及偏移的剩余位(O’)。在示例中,在偏移包括5位的情况下,O’仅包括两位(即,两个最高有效位)。
当搜索数据条目时(例如,在图2的块208中),仅需要搜索与偏移的三个LSB对应的堆栈保存的变量存储区316的行。例如,如果偏移的三个LSB为001,则仅需要在第二行51中查找包含正确的偏移的剩余位(即,O’的对应值)和相同堆栈指针(即,PSP的对应值)的条目。
在各个示例中,可以根据堆栈保存的变量存储区被设置并被索引的方式来改变编译器的操作。例如,在由偏移的子集来索引堆栈保存的变量存储区的情况下,如图5所示,搜索是最有效的,其中在堆栈的顶端处存储变量(即,导致与堆栈指针的偏移的最小值),在这种情况下,能够修改编译器从而使得其在堆栈的顶端处或附近存储最频繁访问的变量。
尽管在图5中所示的示例由偏移或来自偏移的位的子集来索引堆栈保存的变量存储区(其中,在本文中使用的术语子集是指适当的子集),但是在其它示例中,可以使用其它参数来索引堆栈保存的变量存储区。例如,可以由存储堆栈指针的寄存器ID来索引存储区,从而使得与同一寄存器ID对应的所有条目在存储区的同一行/列中。在当从函数调用返回时结合从存储区中清扫条目而使用情况下,仅需要清扫与针对该函数的堆栈指针的特定寄存器ID对应的行/列。在另一示例中,可以由PSP和O字段或那些字段的任意子集/组合/函数的哈希值来索引该存储区,并且其中,可以选择哈希函数和/或字段以提供具有适于该实现方式的尺寸的索引。
在各个示例中,随函数调用之后,堆栈指针可以恢复到在函数调用之前它所具有的值;然而,由于堆栈指针值现在可以被存储在不同的物理寄存器中,所以在数据结构中搜索对应条目时将不会再找到在函数调用之前存储的堆栈保存的值存储区中的任意条目(例如,在图2的块208中)。然而,如果随程序调用之后而将堆栈指针恢复到如函数调用之前的相同物理寄存器,则在堆栈保存的值存储区中的之前存储的数据将仍然是可以使用的。
这能够通过以下示例来说明,在该示例中,在函数调用之前,两个条目被存储在堆栈保存的值存储区中(在图2的块204中):
这两个条目均关于同一堆栈指针值S,该同一堆栈指针值S被存储在物理寄存器P4中。如果在第一情境中,随函数调用之后而将堆栈指针值S恢复但是不将其存储在物理寄存器P4中,而是将其存储在物理寄存器P5中,随后的加载指令:
LD R3[SP+#8]
将导致对包括堆栈指针P5和偏移8的对应条目的搜索。在堆栈保存的值存储区中不存在这样的条目,并且因此必须执行加载(在图4的块214中)。
如果相反,在第二情境中,随函数调用之后而将堆栈指针值S恢复到其原始物理寄存器P4(或者如果实际上在函数调用期间寄存器还没有变化,则堆栈指针被重新映射到原始物理寄存器P4),则对对应条目的搜索将涉及对包括堆栈指针P4和偏移8的条目的搜索。这不导致找到对应条目(第二条目)并且因此虚拟寄存器R3能够被映射到寄存器P8(在块212中)并且不延迟随后的执行而发生从存储器的加载。
在各个示例中,可以使用物理寄存器的低等级控制以确保原始物理寄存器(在堆栈保存的变量存储区中引用的)不是无效的而同时在堆栈保存的变量存储区中引用这些原始物理寄存器。例如,无效逻辑/空闲列表可以具有以下知识:哪些寄存器当前正在被堆栈保存的值存储区所使用并且将不使它们无效。一旦从堆栈保存的变量存储区中清除包含特定寄存器ID的条目,则物理寄存器能够被无效/重使用等。在一些示例中,如果需要的话,无效逻辑/空闲列表可以重使用这些寄存器,并且在这种情况下,通知堆栈保存的值存储区,从而使得它能够移除相关联的条目。
图6示出了对堆栈指针的值进行预测的方法,该方法导致随函数调之后而将堆栈指针恢复到在函数调用之前该堆栈指针所占用的相同物理寄存器。通过使用该方法,即使在函数调用之后仍可以使用上文的方法。
当出现增大堆栈的指令时(块602),连同堆栈的增大的尺寸值一起而存储当前保持有(在增大堆栈之前的)堆栈指针值的物理寄存器的寄存器ID(块604)。寄存器ID和尺寸值可以被存储(在块604中)在新的数据结构中,该新的数据结构可以被设置为保持N对值(其中,N是设计选择)。该数据结构还可以采取堆栈的形式(即,LIFO或FILO数据结构),其中,严格地按照条目被添加到堆栈中的相反顺序来移除条目,并且在本文中可以被称作“预测堆栈”以将它与在图1的块602和606中出现的指令所提到的主堆栈(例如,如图2中所示)区分开。
在示例中,增大堆栈的指令(如参见块602中)可以是函数调用(即,进入函数)的结果,并且该指令可以例如采取以下形式:
SUB P8 P2 16
其中,P8是被分配给堆栈指针值的新的物理寄存器的ID,并且其中该新的堆栈指针值要被存储,P2是当前被分配给堆栈指针的物理寄存器的ID(并且因此保持有在向堆栈添加数据之前的堆栈指针值),并且16是堆栈尺寸的增加。
重新参照图1中所示的示例堆栈100,物理寄存器P2保持有对应于箭头104的堆栈指针值,对应于箭头106的新的堆栈指针值被存储在物理寄存器P8,这使得数据(在该示例中,四个变量108,每个32位宽)能够被添加到堆栈100。
如果数据结构(或预测堆栈)原来是空的,则随着上述示例指令之后,该数据结构的内容可以为:
其中,第一列包含寄存器ID而第二列包括尺寸值(以字节计)。尽管该数据结构能够存储为16的尺寸值(其精确地对应于增大堆栈的指令中的尺寸值,其在该示例中为32位(4字节)),指令中的尺寸值将始终是4的倍数(并因此尺寸值中的位的子集将是恒定的并且不需要被存储)。在该示例中,N=4,这是由于在数据结构中存在用于四个条目的空间。将理解到,仅通过示例的方式提供该数据结构的该尺寸和格式,并且在其它示例中,可以不同地设置该数据结构而仍然保持同样的数据对(寄存器ID和尺寸值)。
数据结构所需的存储量(例如,存储器或触发器(flip-flop))取决于N的值以及存储寄存器ID(例如,4或6位)和尺寸值(例如,4或6位)两者所需的位的数量。在一些示例中,N=1,这导致仅需要非常小的存储空间量的数据结构。然而,在其它示例中,N>1,例如,以允许嵌套函数,如上文所描述的那样。
继出现增大堆栈的指令(在块602中)之后,可以出现缩小该堆栈的指令(块606),其可以例如是从函数返回(例如,退出函数)的结果(。缩小堆栈的指令可以例如采用这样的形式:
ADD P9 P8 16
其中P9是被分配给堆栈指针值的新的物理寄存器的ID,并且其中该新的堆栈指针值要被存储,P8是当前被分配给堆栈指针的物理寄存器的ID(并且因此保持有在从堆栈移除数据之前的堆栈指针值),并且16是堆栈尺寸的减少。
响应于检测到缩小堆栈的指令(在块606中),将该指令中的尺寸值与存储在数据结构中的尺寸值进行比较(块608)。为了当前说明的目的,将该指令中的尺寸值与该数据结构中的顶端条目中的尺寸值(即,该数据结构中最新添加的条目)进行比较。
重新参照上文示出的示例数据结构,可以看出在该示例中,尺寸值确实对应(在块608中的“是”),这是由于所存储的值4对应于堆栈尺寸的变化16(由于上文所述的原因,通过将堆栈尺寸的变化除以4而给定在该示例中的数据结构中所存储的值)。响应于检测到对应或匹配(在块608中的“是”),更新寄存器重命名表以示出被分配给堆栈指针值的新的物理寄存器对应于所存储的寄存器ID(块610)(例如,上文示例中的P2),并且并不使用在缩小堆栈的指令中所标识的物理寄存器(上文示例中的P9)。因此,并不一定要执行运算以计算新的堆栈指针值(这是由于其已经被存储在数据结构中所标识的物理寄存器中,例如上文示例中的P2),这节省了ALU运算并且打破了RAW危害(其允许更大的乱序执行)。另外,在缩小堆栈指令中所标识的物理寄存器(例如,上文示例中的P9)可以用于另一目的(例如,其可以保留在空闲寄存器列表上,其中维持有这样的列表)。将来自数据结构的包含有对应的尺寸值的条目从数据结构中移除(块612),在上文示例中,这留下了空的数据结构。
在上文的示例中,缩小堆栈的指令(其被标识在块106中)包含有对应于数据结构中的顶端条目的尺寸值(在块608中的“是”)。然而,如果缩小堆栈的指令为例如:
ADD P9 P8 24
则尺寸值并不对应(在块608中的“否”,因为24/4=6且6≠4)。在没有对应的情况下,清除数据结构中的所有条目(从而该数据结构是空的),并且以正常方式执行缩小堆栈的指令。
如能够从上文描述中看出的,在数据结构中的条目之间执行的比较以及缩小堆栈的指令确保了仅在预测正确的情况下(即,尺寸值对应的情况下)做出预测,并且确保在其它情况下(即,尺寸值不对应的情况下)不对堆栈指针值进行预测。
在刚刚描述的示例中,将缩小堆栈的指令与数据结构中的顶端条目进行比较,然而在下文描述的其它示例中,比较还可以涉及数据结构中的其它条目。
在各个示例中,可以使用对物理寄存器的低级别控制以确保原始的物理寄存器(其在预测堆栈中被引用)并没有失效而同时在预测堆栈中被引用。一种示例方法为传递具有堆栈增大指令的边带(sideband)从而使得之后释放/使无效物理寄存器的逻辑并不释放/使无效在预测堆栈中所引用的保持有堆栈指针的物理寄存器。在另一个示例方法中,维持预测堆栈的逻辑(例如,图8中所示的堆栈指令值预测模块820)发信号示意哪些寄存器正在使用从而使得释放/使无效逻辑并不释放/使无效这些寄存器。一旦将包含有特定寄存器ID的条目从预测堆栈中清除,物理寄存器就可以被无效/重新使用等。
由于预测堆栈中所引用的物理寄存器没有被无效,可以需要额外的物理寄存器,其中物理寄存器的最小数量对应于虚拟寄存器的总数加一,可以在预测堆栈中引用物理寄存器的最大数量(其等于N)。然而,通常处理器可以具有远多于该最小值的物理寄存器。
在上文所述的示例中,缩小堆栈的指令(参见块606中)跟随在增大堆栈的指令(参见块602中),之后而在两者之间没有添加和/或移除其它数据。然而,在一些示例中,例如对于嵌套函数,在将任何数据从堆栈中移除之前,数据可以多于一次被添加到堆栈中并且这可以在能够参考图7所描述的另一个示例中描述。
在该示例中,出现第一增大堆栈的指令(在块602中):
SUB P8 P2 16
并且因此,所以如上文所述,存储有堆栈指针的寄存器ID(P2)以及尺寸值(16/4=4)被存储在数据结构中(在块604中):
如图7中的第一示例堆栈701中所示,四个数据项被添加到堆栈(如由箭头71所指示的)并且将堆栈指针从对应于箭头706的原始值(存储在物理寄存器P2中)更新为对应于箭头708的新值(其存储在物理寄存器P8中)。
所出现的操作堆栈(即,增大或缩小)的下一个指令可以是例如另一个增大堆栈的指令(如参见块602中),如由从图6中的块604至块602的虚线箭头所指示的。在该示例中,增大堆栈的第二指令可以是:
SUB P4 P8 8
并且因此,所以如上文所述,当前堆栈指针的寄存器ID(P8)以及尺寸值(8/4=2)被存储在数据结构中(在块604中):
该新的条目现在被认为是数据结构中的顶端条目。如在图7中的第二示例堆栈702中所示,两个数据项被添加至堆栈(如由箭头72所指示的),并且将堆栈指针从与箭头708对应的值(如在物理寄存器P8中所存储的)更新至于箭头710对应的新值(其存储在物理寄存器P4中)。
随后可以出现缩小堆栈的指令(在块606中):
ADD P3 P4 8
将该指令中的尺寸值(8)与数据结构中的顶端条目中的尺寸值进行比较(在块608中),并且从该示例中可以看出存在对应(在块608中的“是”,因为8/4=2并且2=2)。因此存储有堆栈指针的物理寄存器的映射被更新为数据结构中的顶端条目中的寄存器ID(P8)(在块610中)并且将顶端条目从数据结构中移除(在块612中),这留下:
如在图7中的第三示例堆栈703中所示,从堆栈移除两个数据项(如由箭头73所指示的),并且将堆栈指针从对应于箭头710的值(存储在物理寄存器P4中)更新为对应于箭头708的值(其之前存储在物理寄存器P8中)。
所出现的操作堆栈(即,增大或缩小)的下一个指令可以是例如另一个缩小堆栈的指令(如参见块606中),如由图6中的块612至块606的虚线箭头所指示的。在该示例中,缩小堆栈的该第二指令可以是:
ADD P7 P8 16
将该指令中的尺寸值(16)与数据结构中的顶端条目中的尺寸值进行比较(在块608中)并且从该示例中可以看出存在对应(在块608中的“是”,因为16/4=4并且4=4)。因此存储有堆栈指针的物理寄存器的映射被更新为数据结构中的顶端条目中的寄存器ID(P2)并且将顶端条目从数据结构中移除(在块612中),这留下空的数据结构。
如在图7中的第四示例堆栈704中所示,将四个数据项从堆栈中移除(如由箭头74所指示的),并且将堆栈指针从对应于箭头708的值(存储在物理寄存器P8中)更新为对应于箭头706的值(其之前存储在物理寄存器P2中)。
在该示例中,存在多个增大堆栈的指令并且跟随着多个缩小堆栈的指令。该示例可以例如对应于嵌套函数,例如其中在指令的顺序方面:
SUB P8 P2 16
SUB P4 P8 8
ADD P3 P4 8
ADD P7 P8 16
外部的指令对对应于第一函数而内部的指令对对应于第二函数,该第二函数为第一函数内部的嵌套函数。
当在其它函数中存在许多嵌套函数的情况下,本文中所描述的方法仍然适用;然而,这些方法可能需要更大的N值(即,更大的数据结构深度)从而使得可以存储更多的条目而不会空间不足。在上文所述的示例中,所需的N值等于或超过2以便使得该数据结构不会溢出。
在函数调用被足够深地嵌套以使得预测堆栈溢出(例如,对于所使用的嵌套程度来说N太小)的示例中,则预测堆栈中最老的信息将会丢失(并且它们的对应物将不会被预测);然而,最新的数据将得以留存并且所做出的预测将继续为正确的。
图8示出了可以实现本文中描述的方法的示例乱序处理器800的示意图。在该示例中,处理器800是单线程处理器,然而,方法也可应用于多线程处理器(其中,其中每个线程都会使用单独的堆栈指针而维持单独的堆栈)。
处理器800包括提取级302、解码和重命名级304、重新排列缓冲区306、提交级308、一个或多个功能单元310、812以及缓存/存储器814,每个功能单元包括一个或多个执行流水线。处理器800进一步包括寄存器堆(RF)816和寄存器重命名映射312,由解码和重命名级304(或者由解码和重命名级304内的寄存器重命名模块314)来维护寄存器重命名映射312。
提取级302被配置为由程序计数器(PC)指示而从程序中(以程序次序)提取指令。一旦提取到指令,该指令就被提供给解码和重命名级304,解码和重命名级304被设置为翻译该指令并且执行寄存器重命名。特别地,每个指令可以包括寄存器写入操作;一个或多个寄存器读出操作;和/或算术运算或逻辑运算。寄存器写入操作向目的寄存器进行写入而寄存器读出操作从源寄存器进行读出。在寄存器重命名期间,用物理寄存器替代(或重命名)指令中提及的每个虚拟寄存器(例如,源寄存器和目的寄存器)。
对于寄存器写入操作,为所提及的虚拟寄存器(例如,目的寄存器)分配未使用的(或可用的)物理寄存器。可以将任何分配存储在寄存器重命名表312中,其中,寄存器重命名表312是示出了每个虚拟寄存器与被分配给程序流中的指令的物理寄存器之间的映射的数据结构。对于寄存器读取操作,针对特定虚拟寄存器(例如,源寄存器)的正确的物理寄存器可以从由该虚拟寄存器所索引的寄存器重命名表312中的条目来确定。
可以在解码和重命名级304内实现上文参照图6和图7描述的方法,并且在示例中,解码和重命名级304可以包括执行在图7中示出的以及上文描述的方法的堆栈指针值预测模块820。如上所述,模块820(或解码和重命名级304)维护在本文中提到的数据结构作为预测堆栈822。
在指令通过解码和重命名级304之后,该指令被插入到记录器缓冲区306(ROB)中并被派送到功能单元310、812以进行执行。指令被派送到的功能单元310、812可以基于指令的类型。重新排列缓冲区306是使得指令能够被以乱序执行而顺序地提交的缓冲区。重新排列缓冲区406保持有被以程序次序插入重排序缓冲区,但是能够由功能单元310、812乱序地执行ROB 306内的指令。在一些示例中,重新排序缓冲区306能够被形成作为循环缓冲区,循环缓冲区具有指向ROB 306中的最旧指令的头和指向ROB 306中的最新指令的尾。以程序次序将指令从重新排列缓冲区306输出到提交级308。换言之,当已经执行指令时,从ROB 306的头输出该指令,并且头被递增至ROB 306中的下一指令。从重新排列缓冲区306输出的指令被提供至提交级308,提交级308将指令的结果提交至寄存器/存储器。
每个功能单元310、812负责执行指令并且可以被配置为执行指定类型的指令。例如,在图8中示出加载-存储单元812,并且其它功能单元310可以整数单元、浮点单元(FPU)、数字信号处理(DSP)/单指令多数据(SIMD)单元或乘积累加(MAC)单元中的一个或多个。整数单元812将数据读取到L1缓存和存储器等并且读取来自L1缓存和存储器等的数据。在一些实例中,加载-存储单元可以计算地址,并且它可以(或者可以不)包含L1缓存并执行数据/标签RAM查找。整数单元执行整数指令,FPU执行浮点指令,DSP/SIMD单元具有同时对多个数据点执行同一操作的多个处理元件,并且MAC单元计算两个数的乘积并将该乘积加到累加器。功能单元内的流水线可以具有不同的长度和/或复杂度。例如,FPU流水线一般比整数执行流水线更长,因为它一般而言执行更复杂的操作。
在执行从解码和重命名级304接收的指令的同时,每个功能单元310、812执行到一个或多个共享寄存器堆816的物理寄存器的读和写。
处理器800还可以包括图8所示的那些功能元件之外的功能元件。例如,处理器可以进一步包括分支预测器,分支预测器被配置为在已知导致潜在的流改变的指令(例如,分支指令)的情况下预测程序流将采取哪个方向。分支预测是有用的,因为在已知分支指令的结果之前它使得指令能够被处理器800推测地执行。当分支预测器准确地预测程序流之前,这改善了处理器800的性能。然而,如果分支预测器不正确地预测分支方向,则出现误预测,在程序能够继续之前需要纠正该误预测。为了纠正误预测,禁止发送至ROB 306的推测性的指令,并且提取级302开始从该正确的程序分支提取指令。
将理解到,其它处理器可以不包括图8中所示的所有功能元件(即,可以省略图8中所示的功能元件中的一个或多个),并且在一些示例中,可以包括图8中未示出的附加功能元件。
除了由解码和重命名级304所维护的预测堆栈822之外,在一些示例中,还可以存在由加载-存储单元812所维护的进一步的新数据结构。该新的数据结构(其在本文中还可以称为堆栈指针缓存)存储堆栈指针物理寄存器ID以及实际的堆栈指针值(存储在对应的物理寄存器中)。在各个示例中,堆栈指针缓存存储最后M个堆栈指针物理寄存器ID以及对应的堆栈指针值,并且在一些实例中,M=N。
图9是由加载-存储单元812实现的堆栈指针值缓存的示例方法的流程图,该方法可以与由解码和重命名级304实现的在图6中示出的方法相结合使用,或者该方法可以独立于图6所示的方法使用。如图9中所示,当例如由解码和重命名级304检测到堆栈指针发生变化时(块902)(其中解码和重命名级304通知加载-存储单元812),将当前堆栈指针物理寄存器ID以及该堆栈指针的值存储在堆栈指针缓存中(块908)。将理解到,这可以以多种不同方式来实现,例如,跟随堆栈指针的改变可以立即存储新的物理寄存器ID和值,或者替选地,在改变之前可以立即存储旧的物理寄存器ID和值。在各个示例中,存储物理寄存器ID可以使用存储器的6位而存储堆栈指针值可以使用32位。
在堆栈指针缓存的尺寸有限的情况下,其可以被设置为存储M个数据对(其中该对包括物理寄存器ID和堆栈指针值)。在该尺寸有限时,在没有空间存储新的值的情况下(在块904中的“是”),存储新的数据对可以需要丢弃最老的存储的值对(块906)。
当物理寄存器ID被移动到“空闲寄存器列表”(例如,在解码和重命名级304中)时,移除堆栈指针缓存中的对应条目(块910)。
通过存储物理寄存器ID和值,当堆栈指针接收到用于使用地址中对应的物理寄存器来进行加载/存储的指令时,加载-存储单元已经知道该堆栈指针的值,并且这消除了执行寄存器堆读取的需要(其需要寄存器堆读取端口)。通过缓存堆栈指针的值,可以计算地址通常是堆栈指针加上立即数偏移)而不需要读取端口。这意味着可以针对这些计算而使用有限的ALU,并且这释放了其它功能但与以用于其它操作。
可以通过将新的栏添加到堆栈指针缓存以保持偏移值来扩展图9的方法。该偏移会是距离堆栈指针的偏移(由于存储在存储器中的变量被存储在距离该堆栈指针的固定偏移处)。在这种情况下,如果用于加载/存储的地址为堆栈指针值加上偏移(例如,LD P20[P2+8]),则物理寄存器和偏移可以用于查询并且检测全部32位是否都被缓存在堆栈指针缓存中。
在上文描述的该示例中,所述比较(图6的块608中)将指令(参见块606中)中的尺寸值与预测堆栈中的顶端条目比较。然而,在其它示例中,所述比较可以涉及预测堆栈中多于一个条目。例如,如果操作堆栈的两个连续指令都向堆栈添加数据:
SUB P8 P2 16
SUB P9 P8 8
这导致两个条目中的该结果被存储在预测堆栈中:
如果出现随后的缩小堆栈的指令(在块606中):
ADD P7 P9 24
如果出现随后的缩小堆栈的指令(在块608中)不会导致发现对应(6≠2);然而,与顶端的两个条目的比较会导致对应(6=4+2)并且因此,如图10所示,比较操作(在块608中)可以涉及着眼于多于一个条目。
图10是实现比较操作(图6中的块608)的示例方法的流程图,其中存在可以在尺寸比较中使用的条目数量的阈值T。如果缩小堆栈的指令中的尺寸(在块606中出现)仅要与预测堆栈中的顶端条目相比较,则T=1;然而,比较可以涉及预测堆栈中的多于一个条目,则T>1。如图10中所示,该比较始于变量x=1并且将缩小指令中的尺寸(来自块606)与预测堆栈中的顶端条目进行比较(在块1002的第一次迭代中)。对于T=1,方法如上文所述进行并且如果不存在对应,则清除堆栈中的所有条目(在块614中)。然而如果T>1,则还存在一个或多个比较的迭代,其中将缩小指令中的尺寸(来自块606)与来自堆栈中逐渐增加的数量的条目(在每次迭代中增加一个条目)的尺寸总和进行比较,直到存在对应(当方法进行到图6中的块610时)、或达到阈值而未对应(当方法前进到图6中的块614)、或已使用了预测堆栈中的所有条目而未发现对应(当方法再一次到达图6中的块614)为止。
在比较操作(在块608中)中使用预测表中的多个条目的情况下,例如,如图10所示,使用在比较操作中所使用的所有条目中的最早添加的(即,最旧的)条目(即,x个条目中的最老的条目,该x个条目用于实现对应)来更新堆栈指针的映射(在块610中),并且将用于实现对应的所有条目(即,导致块1002中的“是”的所有x个条目)从预测堆栈中移除(在块612中)。
还可以更新该方法以处理这样的情况:其中缩小堆栈的指令并没有完全撤销预测堆栈中的固定数量的条目的操作,如在图11中所示。图11是迭代方法,并且可以指定迭代的最大数量。例如,如果两个连续的操作堆栈的指令都向堆栈添加数据:
SUB P8 P2 16
SUB P9 P8 8
则这导致两个条目中的该结果被存储在预测堆栈中:
如果出现随后的缩小堆栈的指令(在块606中):
ADD P7 P9 12
则与顶端条目的比较不会导致发现对应(在块608中的“否”,因为12/4=3并且3≠2),但是(假设还没达到迭代的最大数量,在块1107中的“否”)当该尺寸对应于大于所存储的尺寸的值时(在块1101中的“是”,因为3>2),移除预测堆栈中的顶端条目(块1103),并且缩小操作的尺寸减少2(即,减少了刚刚移除的条目的尺寸)以在该示例中给出为1的缩小的尺寸。
在本方法的第二迭代中(再次假设还未达到迭代的最大数量并且堆栈中还有更多的条目,在块1107中的“否”),在缩小操作的尺寸(在之前迭代的块1103中所减少的,在该示例中为1)与所存储的尺寸(在该示例中为4)之间执行另一比较(在块1101中)。在该示例中,当更新后的缩小指令的尺寸小于所存储的尺寸时(在块1101中的“否”,因为1<4),将允许缩小指令正常运行(从而使得在该特定示例中,计算新的堆栈指针并且存储在P7中),并且新的顶端条目中的尺寸将减少缩小指令的尺寸(块1105,例如在该示例中缩小1,这是由于所移除的条目包含值2并且3-2=1)以给出为3的条目尺寸。这导致一个条目被留在预测堆栈:
如果出现随后的缩小堆栈的指令(在块606中):
ADD P6 P7 12
则与顶对条目的比较现在会导致发现对应(由于12/4=3并且3=3)。所以,存储有堆栈指针的物理寄存器的映射被更新(在块610中)为数据结构中的顶端条目中的寄存器ID(P2)并且将该顶端条目从数据结构中移除(在块612中),留下空的数据结构。该缩小堆栈的指令会不需要执行。
在处理器动态地分配存储器的情境中,可以存在作为动态分配的结果而将数据添加到堆栈(导致堆栈指针中发生变化),以及作为函数调用的结果而将数据添加到堆栈(如上所述)并且因此上文参考图6所描述的方法可以被修改并且在图12和图13中示出了两个变型。
在图12中示出的第一示例变型中,条目仍然被存储在预测堆栈中,其为存储器的动态分配的(而非函数调用)结果,该存储器的动态分配在预测堆栈中被标记出(在块604)。这种指令的示例(例如,如参见块602中)为SUB SP SP R5,而非SUB SP SP 8。
在示例中,如果存在将数据添加到堆栈(其作为函数调用的结果)的两个指令,并且其后跟随着将数据添加到堆栈(其作为存储器的动态分配的结果)的一个或多个指令,则预测堆栈可以看起来像是这样:
在该示例中,仅指令序列(其将数据添加到堆栈并且其为存储器的动态分配的结果)中的第一个被存储在预测堆栈中(在块604中)。
如果随后出现缩小堆栈的指令(在块606中),例如:
ADD P7 P9 16
则尺寸的比较(在块1207中)关注在具有与指令中所标识的相同的堆栈指针寄存器ID(例如,本例中的P9)的一个条目之前的条目。在该示例中,存在对应(在块1207中的“是”,由于在包含有P9的条目之前的条目为P8,4,16/4=4并且4=4)并且因此,将堆栈指针的映射更新为寄存器ID P8(在块610中)。然后,将从预测堆栈的顶端处直到并且包括在具有与指令中所标识的相同的堆栈指针寄存器ID(例如,上例中的P8)的一个条目之前的所有条目移除(块1211)。在上例中,则该预测堆栈会仅包含单个条目:
图13中所示的第二示例变型涉及检测与帧指针有关的指令(其表明存储器分配是动态执行的)。在存储器被动态地分配的情况下,可以使用堆栈指针和帧指针两者。不同于当数据被添加堆栈到或从堆栈移除时值发生变化(并且因此在当程序在存储器被动态分配的情况下运行时可以发生变化)的堆栈指针,帧指针可以用于指向堆栈中的固定位置(在程序运行期间,如果动态存储器分配需要堆栈指针)。该固定位置可以是例如如果在函数内并不使用动态位置则堆栈指针将会指向的位置(即,在最后静态分配的存储器区段之后的存储器位置)。
如图13中所示,在该第二示例变型中,以与上文参照图6描述的相同方式将条目添加至预测堆栈(在块602和604中);然而,如果出现基于堆栈指针值而设置帧指针的指令(块1305),例如:
MOV FP SP
其可以用被分配给SP的物理寄存器(本实例中的P9)来更新针对FP的重命名映射条目,则在预测堆栈中添加新的条目(块1306)。在示例中,在检测到与帧指针有关的指令之前的预测堆栈可包括:
随着检测到基于堆栈指针而设置帧指针的指令(在块1305中),该预测堆栈可以包括:
其中,该预测堆栈中的顶端条目包括被分配给堆栈指针的当前物理寄存器(本示例中的P9)以及为零的尺寸值(由于堆栈还没有增大)。该指令MOVFP SP可以被认为是很快将会有动态分配的指示(而非尝试检测动态分配自身)。
本方法接着以与参考图12所描述的类似的方式进行。例如,如果存在增大堆栈的多个后续指令,则预测堆栈可以包括:
P2 |
4 |
P8 |
4 |
P9 |
0 |
P10 |
4 |
P11 |
4 |
预测堆栈中的最新的条目可以来自静态分配,该静态分配来自嵌套函数内。可以以通常的方式来移除这些条目,留下如下的预测堆栈:
这时,如果出现将堆栈从P9中的值缩小4的缩小指令,例如:
ADD P12 P9 16
则可以将堆栈指针重映射到P8(由于P9是完成SUB P9 P8 16之后的SP)、可以放弃该指令、并且可以从预测堆栈中移除至少2个条目(在块1211中)以留下仅一个条目。
将理解到,尽管图12-13的描述是指与预测堆栈中的单个条目的比较,但是这些变型可以与参考图10-11所描述的变型相结合,例如从而使得可以将缩小堆栈的指令中的尺寸(如参见块606中)与预测堆栈中的多于一个条目进行比较(在块608或1207中)。
在图12和图13的进一步变型中,可以使用标记(在预测堆栈中的每个条目中)以表明是否作为函数调用的结果而添加了指令。在这样的示例中,修改块1211从而使得在预测堆栈顶端处的所有被标记的条目被移除。
图14是检测到中断时所使用的方法的流程图(块1402),并且可以结合之前描述的方法中的任意方法来使用该方法。当发生中断时,堆栈指针被存储在存储器中并且接着在退出中断时恢复。由于中断可以已经操作了堆栈指针的值,所以存储在堆栈指针中的条目可以不再有效并且因此清除预测堆栈以移除所有条目(块1404)。这可以扩展到包括检测到从存储器加载了堆栈指针值的任何情况。
上文描述的方法关于使用寄存器重命名的处理器。在不适用寄存器重命名的情况下该方法也是可应用的;然而在这样的应用中,预测堆栈存储当前堆栈指针值(在堆栈增大之前的堆栈指针值)以及堆栈增大的尺寸值(在块1104中),如图15中所示。这比使用寄存器ID效率更低,这是因为堆栈增大需要更多的存储空间(例如,它可以是32位的值)。然后,该方法如图6中所示以及上文描述的那样继续,除了代替在缩小指令中的尺寸(参见块606中)与预测堆栈中的一个所存储条目(或多个条目)之间存在对应的情况下更新堆栈指针的映射(在块610中)(在块608中的“是”),堆栈指针自身被更新为所存储的值(块1510)。将理解到,上文参照图6所描述的变型(例如,如图10-13中所示)还可应用于图15中所示的方法。
在上例中,存储有堆栈指针寄存器ID和堆栈的增大的尺寸值的数据结构被描述为FILO。然而将会理解,其可以替换地使用缓存结构而实现。
尽管上文所述的方法关于对堆栈指针值的预测,但是所描述的技术还可以用于检测被预测为可逆的任何指令,并且如果随后出现该指令的逆转,则可以使用老的物理寄存器。在这样的示例中,所使用的数据结构可以使用缓存结构而非FILO以存储关于可能可逆的所有函数的数据。由于针对可能可逆的每个操作都存储了数据(例如,物理寄存器ID和常数值,尽管根据特定的操作还可以有其它字段),而不是仅有在随后被逆转的指令,所以有必要着眼于数据结构中的非最新存储的条目。
本文中描述的方法可以结合具有与C类似的调用惯例的编程语言(这覆盖了大多数语言)而使用,其中这样的编程语言在存储器中使用堆栈以用于在程序范围的该部分内进行存储。可以修改本文中的方法以供结合其它语言使用。
通过将对保存到堆栈的值进行预测的方法与对堆栈指针进行预测的方法相结合,在堆栈保存的值存储区中保存的数据可以在更多情形下有用(即,可以存在可以在堆栈保存的值存储区中找到对应条目的更多情形),并且因此可以存在增加数量的场合,在这些场合能够避免与从存储器加载值相关联的延迟。
一种对使用上文参照图6-16所描述的方法的替选方案,可以存储堆栈结构(而非上述预测堆栈),该堆栈结构仅存储堆栈指针所增大的偏移(而非SP物理寄存器ID和SP值),并且这可以被称作“偏移堆栈”。在使用偏移堆栈的情况下,还修改堆栈保存的值预测存储区,从而使得存储表示偏移堆栈的顶端条目的ID,而非存储用于存储堆栈指针的物理寄存器的ID(PSP)。这减少了偏移堆栈的存储空间(相比于预测堆栈)和堆栈保存的变量存储区的存储空间,并且使得上文参照图1-5所描述的方法能够跟随函数调用而被使用,但是不允许堆栈指针值的预测。
重新参照图2,在这种实现方式中,在数据结构中存储的数据条目(在块204中)包括偏移堆栈的当前ID(IDOFF)、连同存储有变量被存储到堆栈(P)之前的该变量的值的物理寄存器的寄存器ID一起、以及与变量被存储处的当前堆栈指针的偏移(O)。当随后出现参照堆栈指针而从堆栈加载变量的指令时(在块206中),搜索堆栈保存的变量存储区(在块208中)以查找对应条目,并且在该示例中,通过比较偏移O和偏移堆栈的当前IDIDOFF来确定对应条目。在找到对应条目的情况下(在块210中的“是”),将加载指令中引用的虚拟寄存器(参见块206中)映射到由对应条目中的P所给定的寄存器ID。
还可以修改上文描述的方法使得它们能够用于更一般的加载/存储(即,不直接参照堆栈指针的加载/存储)。当用于数据项的指针被传递到函数中时,它将通常是堆栈指针(来自之前的函数)的值并且具有所添加的偏移。然后,这能够用在该函数内部(用于存储,或者用于加载)。
在上述方法的这种变型中,存储关于哪些物理寄存器处于距堆栈指针物理寄存器特定已知的偏移处(例如,在相当于图2的块204的步骤中)的信息,并且然后该信息能够用于检测匹配(例如,在相当于图2中的块210的步骤中)。例如:
ST P1[P2+#8] (P2=SP)
ADD P3 P2 8 (创建到数据的指针)
#函数调用
LD P4[P3+#0] (在P2,8搜索表,因为已知P3=P2+8)
或者当从函数出来时:
ADD P3 P2 8 (创建到数据的指针)
#函数调用
ST P1[P3+#0] (添加到表以作为P2,8,因为我们知道P3=P2+8)
#函数返回
LD P4[P2+#8] (在P2,8搜索表)
在两种情况下使用该变型均将找到匹配,即使使用上文描述的之前的方法它们不匹配。
尽管上文关于单个处理器(非多线程)描述了这些方法,但是这些方法也可以应用于多线程和/或多核处理器,这是由于多线程和/或多核处理器不共享堆栈空间,并且因此上述方法可以针对每个线程/核而独立地运行。
上述方法全部依赖于对寄存器重命名的使用。上述方法涉及存储数据条目(例如,在块204中),在该数据条目中表示出了元素PSP、P和O,并且然后通过PSP和O来搜索这些数据条目以找到P。在对此(上文所述的)的变型中,数据条目可以表示元素IDOFF、P和O,并且然后可以通过偏移ID(IDOFF)和O来搜索这些数据条目以找到P。然而,可以修改这些方法以便在没有寄存器重命名的情况下使用。特别地,在图2的块204中,存储寄存器的值V,而不是存储物理寄存器P,并且可以使用上文所述的偏移堆栈并且可以存储所存储的IDOFF的值或堆栈指针的值VSP,而不是存储与堆栈指针对应的寄存器ID(PSP)。当搜索对应条目时(例如,在图2的块208中),通过堆栈指针的值(VSP)和O或者偏移ID(IDOFF)和O来搜索数据条目以找到V(而非P),并且该值V用于更新存储有变量的寄存器(例如,在与图2的块212对应的块中)。
能够看出无论是否使用了寄存器重命名,该方法使用在堆栈保存的变量存储区中的对应条目中存储的信息来更新虚拟寄存器的有效值。在使用寄存器重命名的情况下,通过更新寄存器重命名映射来更新虚拟寄存器的有效值(在块212中),而在不使用寄存器重命名的情况下,通过将值(来自堆栈保存的变量存储区中的对应条目)写到寄存器来更新虚拟寄存器的有效值。
本文中使用的术语“处理器”和“计算器”是指具有处理能力从而使得其能够执行指令的任何装置、或其部分。术语“处理器”可以例如包括中央处理器单元(CPU)、图形处理单元(GPU或VPU)、物理处理单元(PPU)、数字信号处理器(DSP)、微处理器等。本领域技术人员将认识到,这种处理能力被结合到许多不同装置中,并且因此术语“计算机”包括机顶盒、媒体播放机、数字广播、PC、服务器、移动电话、个人数字注意和许多其它装置。
本领域技术人员将认识到,所利用的用于存储程序指令的存储装置可以跨网络分布。例如,远程计算机可以存储被描述为软件的过程的示例。本地或终端计算机可以访问远程计算机并且下载该软件的一部分或全部以运行该程序。或者,本地计算机可以根据需要而下载软件的一些部分、或在本地终端处执行一些软件指令并在远程计算机(或计算机网络)处执行一些软件指令。本领域技术人员还将意识到通过利用本领域技术人员已知的传统技术,可以由专用电路(例如,DSP、可编程逻辑阵列等)来执行软件指令的一部分。
存储有用于实现所公开的方面的机器可执行数据的存储器可以是非瞬时性介质。非瞬时性介质可以是易失性或非易失性的。易失性非瞬时介质的示例包括基于半导体的存储器,例如SRAM或DRAM。可以用于实现非易失性存储器的技术的示例包括光和磁存储器技术、闪速存储器、相变存储器、电阻式RAM。
对“逻辑”的特别引用是指执行功能或多个功能的结构。逻辑的示例包括被设计为执行那些功能的电路。例如,这种电路可以包括晶体管和/或在制造处理中可用的其它硬件元件。通过示例的方式,这种晶体管和/或其它元件可以用于形成实现和/或包含存储器(例如,寄存器、触发器、或锁存器)、逻辑运算器(例如,布尔运算)、数学运算器(例如,加法器、乘法器或移位器)和互连的电路或结构。这种元件可以被提供作为定制电路或标准单元库、宏或其它级别的抽象。可以以指定设置将这样的元件互连。逻辑可以包括作为固定功能的电路,而电路能够被编程以执行一个或多个功能;可以从固件或软件更新或控制机制来提供这种编程。所标识的用于执行一个功能的逻辑还可以包括实现组成性函数或子过程的逻辑。在示例中,硬件逻辑具有实现固定函数操作、或运算、状态机或过程的电路。
如将对本领域技术人员显而易见的是,本文中所给出的任何范围或装置值可以被扩展或改变而不会失去所寻求的效果。
将会理解上文所述的益处和优点可以关于一个实施例或可以关于几个实施例。实施例并不限于解决所陈述的问题中的任意或全部问题的那些实施例或者具有所陈述的益处和优点中的任意或全部优点的那些实施例。
对“一个”的任意引用是指那些物品中的一个或多个。本文中使用“包含”以意味着包括所标识的方法块或元件,但是这样的块或元件并不包括排他性列表并且装置可以包含额外的块或元件并且方法可以包含额外的操作或元件。此外,块、元件和操作它们自身并不是隐含地封闭的。
可以以任何合适的次序来执行本文中所描述的方法的步骤,或在合适的情况下同时来执行本文中所描述的方法的步骤。图中的框之间的箭头表示方法步骤的一个示例顺序但是并不是要排除其它顺序或并行执行多个步骤。另外,可以从任意方法中删除单独的块而不背离本文中所描述的主题的精神和范围。上述的任何示例的方面可以与所描述的任何其它示例的方面相结合以形成另外的示例而不失去所寻求的效果。在示出了由箭头来连接图的元件的情况下,将会理解这些箭头仅表示元件之间的一个示例通信流(包括数据和控制消息)。元件之间的流可以在任一方向或两个方向。
将理解的是,仅通过示例的方式而给出对优选实施例的上述描述并且可以由本领域技术人员进行各种修改。尽管上文使用一定程度的特定性或参考一个或多个单独的实施例而描述了各个实施例,本领域技术人员能够对所公开的实施例进行多种变化而不背离本发明的精神或范围。