第四章 应用级编程模型
本章以应用级程序员的角度来描述体系结构的各项功能,在这里,手册将安腾处理器的各条指令以相关的功能被划分成多个组,并对每一组指令的行为进行了综述。除非特别说明,所有的立即数在使用前都被符号扩展至64位 。另外,有关浮点编程模型的内容单独在第五章讲述,若要查阅有关安腾指令的详细信息,请参阅第三卷:指令集参考手册。
本章描述的编程模型所包含的几块内容列如下:
l 通用寄存器栈
l 整型计算指令
l 比较指令和预判指令
l 存储器访问指令和投机执行
l 转移指令和转移预测
l 多媒体指令(Multimedia Instructions)
l 寄存器内容传输指令(Register File Transfer Instructions)
l 字符串处理和Population Count
l 特权级跃迁
4.1 寄存器栈正如通用寄存器一节描述的那样,整个通用寄存器堆被分为静态集和堆栈集两个子集,静态集包括从GR0到GR31的一共32个寄存器,对所有的过程都是可见的,而堆栈集则做为每个过程的私有资源,从GR32开始,大小可变,范围从0到96。在过程调用和返回时连带的寄存器重命名的功能使整个寄存器栈机制成为现实,但寄存器重命名机制对应用级程序是不可见的,且在执行IA-32指令集时,寄存器栈不可用。
在各个过程的临界点上,静态集寄存器的内容必须根据软件的需要被保存或被恢复,而堆栈集寄存器的内容是由寄存器堆栈引擎来自动保存和恢复的,这一操作不需要软件显式的插手管理,另外,对所有过程可见的其它的寄存器堆的内容也必须像静态集寄存器那样根据软件的需要被保存或被恢复。
4.1.1 寄存器栈操作 对于一个给定的过程,其可见的堆栈寄存器集便叫做这个过程的寄存器栈帧。帧又被进一步划分成两个大小可变的区域:本地区和输出区。在一个过程调用操作执行后的第一时间内,新的被激活的帧的本地区的大小为零,而输出区的大小则等于调用者的输出区大小,并覆盖调用者输出区。我们可以用alloc指令来重新设置一个帧的本地区和输出区,alloc指令声明了若干个立即数,这些立即数用于决定帧的大小(sof)和本地区的大小(sol)。
说明:在汇编语言当中,alloc指令使用三个立即数来表示sol的值和sof的值:inputs,locals,outputs。Sol的大小为inputs和locals两个立即值加起来的和,而sof的值就是三个立即值的总和。
三个立即数当中的sof声明了对应于当前过程中可见的整个堆栈集的大小,而sol则声明了整个堆栈集中本地区的大小,因此输出区的大小也就是前两个立即数的差。被激活的过程对应这些参数的状态在当前帧标记当中统一维护管理。
读取当前寄存器栈以外的内容所返回了值是无意义的,而对当前寄存器栈以外的内容进行写操作则会导致非法操作错误。
当执行
br.call或
brl.call指令时,当前帧标记的内容就会被拷贝到先前过程状态应用寄存器(PFS)中的先前帧标记这个域中,然后,被调用者的帧按下列顺序开始创建:
l 堆栈集寄存器被重命名,如此一来,被调用的过程眼中的GR32则成为调用者过程输出区中的第一个寄存器。
l 新创建帧中的本地区大小被置为零。
l 被调用者拥有的帧的大小被设为调用者输出区的大小
调用者寄存器栈帧中输出区的内容对于被调用者来说是可见的。这种区域交叠的设计使得当过程调用的时候,过程需要传递的参数或返回值全部在寄存器中不要动就可以直接从一个过程的帧传到另一个过程的帧。
一个过程的帧可以通过发射alloc指令来动态的重新设置大小。发射alloc指令并不会导致寄存器重命名,只是改变了寄存器栈帧的大小以及帧中本地区和输出区之间的划分情况。一般来说,当一个过程被调用的时候,它就会分配若干个寄存器以供本地过程使用(其中包括了调用者输出区中传递过来的寄存器),再加上自己的输出区(用于将参数传给它要调用的过程),此刻,新分配的寄存器中的值(包括Nat位)是无意义的。
当执行
br.ret指令的时候,CFM开始从PFM中恢复原来的值,并且寄存器重命名也恢复到调用者过程原来的设置状态。PFM是过程的私有状态数据,必须由non-leaf过程来负责保存或恢复值。 且CFM不可被应用级程序直接的访问,对其数据的更新操作只能通过执行调用,返回,alloc,cover以及clrrb指令来实现。
图4-1(注:图略
)展示了从过程procA(调用者)到procB(被调用者)调用过程中寄存器栈的行为。在此图中寄存器的状态时间段一共有四段:调用前,调用后第一时间内,procB执行了alloc指令后,以及procB返回procA之后。
有效的利用寄存器栈操作对于应用级程序来说,主要就是一个alloc指令和PFM的存储及恢复指令就可以了。只有象用户级线程分包、调试器等这类专门的应用软件才需要有关RSE(寄存器栈引擎)的详细知识。
4.1.2 寄存器栈指令指令alloc用于改变当前寄存器栈帧的大小,alloc必须是指令组中的第一条指令,否则,产生的结果是不可预料的。Alloc指令对寄存器栈帧的影响对于同一指令组中的所有指令包括alloc自身都是可见的。如果alloc指令的限定谓词不是PR0,则会引起非法操作错误。另外还有一点,alloc指令并不影响分配的寄存器中的Nat位,当寄存器栈帧扩展的时候,新分配的寄存器的Nat保持它们原来的值。
除alloc指令以外,还提供了三条指令用于控制寄存器栈的状态。一般在线程和上下文切换时,当为寄存器栈提供后备存储器成为必要的时候使用这三条指令。第二卷第六章“寄存器栈引擎”中有关于RSE显式管理的详细说明。
指令flushrs强迫所有先前的寄存器栈帧内容全部从寄存器中卸出到后备存储器中来。在激活的帧的内容全部从存储器中拷回物理寄存器之前指令是无法执行的,但这内容并不包括由RSE负责卸出到后备存储器中的当前帧的内容。指令flushrs必须是指令组中的第一条指令,否则引起的结果不可预料。Flushrs指令的谓词必须为真。
指令cover创建一个大小为零的新帧,这个帧在当前帧的位置之上被创建,与当前帧没有交叠区域。这种情况下的先前帧的内容会被自动的保存。指令cover必须为指令组中的最后一条指令,否则引起的结果不可预料,cover指令的谓词必须为真。
指令loadrs确保寄存器线栈中被声明部分的内容留在物理寄存器中,除非当RSC应用寄存器中的loadrs域中声明的字节数全部从后备存储器装载到物理寄存器中,否则当前指令无法执行。因此,通过对RSC.loadrs置入零值,可以使得当前帧之外的所有栈寄存器的内容在被使用之前都得先从后备存储器中全部加载。说明一点,事前没有被RSE卸出到后备存储器中的当前帧之外的栈寄存器的内容是不需要保存到存储器的。指令loadrs必须为指令组中的第一条指令,否则引起的结果不可预料,loadrs指令的谓词必须为真。
表4-1(注:表格略)列出了与寄存器栈当关的可见的系统状态属性,表4-2总结了有关的寄存器栈管理指令,其中,会影响到寄存器栈的调用-返回转移指令将在“转移指令”一节讲解。