新闻中心

EEPW首页 > 模拟技术 > 设计应用 > Xtensa处理器窗寄存器函数调用机制与应用

Xtensa处理器窗寄存器函数调用机制与应用

作者:时间:2012-10-10来源:网络收藏

现代为了更好的支持高级编程语言的高效编译,通常所拥有的通用的数目都有16个甚至32个之多,如此多的在比较复杂的应用程序上实现深度嵌套调用的时候,为了保证程序的正确执行,要频繁的进行入栈和出栈操作,这样频繁的堆栈存储器访问将明显降低应用程序的性能,为有效解决这一问题,tensilica的架构设计了一种Windows旋转方式的寄存器管理机制,将逻辑寄存器和物理寄存器分开,在调用的时候通过windows滑动切换逻辑寄存器,从而避免寄存器覆盖,减少压栈和出栈的操作,更大限度的提高性能。

本文引用地址:http://www.eepw.com.cn/article/185680.htm

  以一个MP3解码器为例(如表1),假设外部存储器的访问的R/W等待cycles分别为100和20,可以看到采用Call8的windows旋转大幅减少MCPS到9%之多。

表1:MP3解码器。

表1:MP3解码器

  那么Windows寄存器机制是如何工作的,它又有那些典型应用呢? 本文将详细阐述这一主题。

  寄存器Windows调用机制原理

  1.AR物理寄存器环形Buffer

  该方法的基本实现原理是用更多的物理AR寄存器组成一个环形的buffer,这些物理寄存器每4个为一组(pane),用一个WindowStart的每个比特依次表示是否该组作为逻辑寄存器窗口的起始位置或者占用,当前的逻辑寄存器的起始位置则用WindowBase状态寄存器来表示。如图1,在发生调用的时候则通过修改WindowBase寄存器,滑动逻辑寄存器窗口,设置相应的WindowStart比特标识当前逻辑窗口在环形物理AR寄存器buffer中的位置。这样父子函数看到的是不同的物理寄存器,避免了寄存器的压栈和出栈。要说明的是,如果AR物理寄存器的数目为NAREG,则WindowStart的比特数则为NAREG/4,而WindowBase的比特位数则为log(NAREG/4),如图1所示,物理寄存器数为32,则WindowStart比特数为8,WindowBase比特数则为3.

图1:Windows AR寄存器环形buffer.

图1:Windows AR寄存器环形buffer.

  2.Windows ABI函数调用规范

  以每4个寄存器(pane)为单位,函数调用的时候窗口可以滑动4个、8个、或者12个物理寄存器,分别可以用call4、call8、call12指令来实现,而最典型的应用则为call8,在c语言层面,编译器通过XPG的core配置,可以为函数调用分别产生非windows机制的call0和call8,那么call8的Windows ABI函数调用规范是怎样的呢? 参考图2,左上角说明的是子函数调用约用规范,a0被用来保存返回地址,a1则为sp堆栈指针,a2~a7则用来传递函数入参,参数超过6个的时候则需要使用堆栈了,以对调用者函数和被调用函数来说,a0~a7为独立的寄存器,可以自由使用,而a8~a15则为scratch寄存器,随时会被子函数使用,调用者函数如果要使用,则在调用子函数前要压栈保存。

图2:Window ABI调用规范。

图2:Window ABI调用规范。

  为方便寄存器正常的保存与恢复,以及调用栈的高效回溯,有必要对函数的Frame栈空间做统一的安排,在call8的Windows ABI规范下,Tensilica进行了如下设计(如图3)。

图3:Windows ABI堆栈布局。

图3:Windows ABI堆栈布局。

  每级函数FrAME下包含有Base Area用于存储其父函数的基本寄存器a0~a3,可能的extra area保存其子函数的扩展寄存器a4~a7(call8),或者a4~a11(call12),函数局部变量(非寄存器变量)和alloc分配空间,及用于传子函数所需要的栈空间等等。

  当较新的深度函数Fun(i)的寄存器窗口覆盖到过去的函数Fun(p)时,基本寄存器a0~a3保存到Fun(p+1)的basic area,额外的寄存器则存入Fun(p)的extra area,当函数Fun(p+1)返回时,如果检测到underflow则相应地将base area和extra area的寄存器恢复到Fun(p)的活动窗口,读者可以参考Tensilica的代码体会一下,这样的布局在压栈和恢复的时候代码是最高效和节省空间的。

3.Windows寄存器覆盖问题

  物理AR寄存器的数目是有限的,典型情况下,32个物理寄存器发生深度为3次,64个AR发生7次的函数调用后将会覆盖到原来的函数寄存器窗,那么如何有效检测和处理寄存器overflow问题呢?

  寄存器的覆盖检测只发生在如下两种情况:

  函数调用时,参考如下硬件semanTIcs:

  CALLn/CALLXn

  PS.CALLINC ← n32

  AR[n||2'b00] ← n || (PC + 3)290

  ENTRY s,imm12

  WindowCheck (00,PS.CALLINC,00)

  if as > 3 | PS.WOE = 0 | PS.EXCM = 1 then

  -- undefined operatiON

  -- may raise illegal instruction exception

  else

  AR[PS.CALLINC||s10] ← AR[s]- (017||imm12||03)

  WindowBase ← WindowBase + (02||PS.CALLINC)

  WindowStartWindowBase ← 1

  endif

  在发生函数调用,执行call指令的时候,窗递增值(call4、call8、call12分别对应1、2、3)存入PS状态寄存器的CALLINC域,在进入函数的入口处ENTRY指令将首先进行Window重叠检测,条件满足的时候将触发相应的windows overflow异常,引导程序进行覆盖寄存器的入栈保护。

  正常模式下函数内部指令的寄存器引用,如xxx ar,as,at,处理器在非异常模式下将进行正常的window检测,否则产生非法指令异常。

  4.Windows寄存器检测方法

  寄存器覆盖检测通过如下硬件semantic实现:

  WindowCheck

  n ← if (wr ≠ 2'b00 or ws ≠ 2'b00 or wt ≠ 2'b00) and WindowStartWindowBase+1 then 2'b01

  else if (wr1 or ws1 or wt1) and WindowStartWindowBase+2 then 2'b10

  else if (wr = 2'b11 or ws = 2'b11 or wt = 2'b11) and WindowStartWindowBase+3 then 2'b11

  else 2'b00

  if CWOE = 1 and n ≠ 2'b00 then

  PS.OWB ← WindowBase

  m ← WindowBase + (2'b00||n)

  PS.EXCM ← 1

  EPC[1] ← PC

  nextPC ← if WindowStartm+1 then WindowOverflow4

  else if WindowStartm+2 then WindowOverflow8

  else WindowOverflow12

  WindowBase ← m(注:和Overflow跳转并行)

  endif

  通过深入解析如上原语,有如下注意要点:任何地方引用a0~a3不会产生windows异常,因此在用户的c或者汇编代码里可以任意使用,为什么呢? 因为在a0~a3引用的任意环境里,当前函数的逻辑窗里的物理寄存器,要么是无覆盖安全到达,要么是经过了函数调用entry指令触发windows overflow异常,在异常里,a0~a3的所在物理AR寄存器已经安全地压栈保存了。

  a15~a4之间的高位寄存器(比如a15)引用会触发低位寄存器(如a4)的寄存器覆盖检测,哪怕没有指令显式的应用低位寄存器,触发的顺序将是先进行overflow4,overflow8,至overflow12,从而最有效和最安全地保存活动寄存器。通过了解以上两点,读者可以深入理解Tensilica提供的高效XTOS代码,透彻体会相关代码的精妙之处。

c语言相关文章:c语言教程



上一页 1 2 下一页

评论


相关推荐

技术专区

关闭