当前位置:首页 > 模拟 > 模拟
[导读]现代处理器为了更好的支持高级编程语言的高效编译,通常处理器所拥有的通用寄存器的数目都有16个甚至32个之多,如此多的寄存器在比较复杂的应用程序上实现深度嵌套调用的时候,为了保证程序的正确执行,寄存器要频繁

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

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

表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.

  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调用规范。

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

图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代码,透彻体会相关代码的精妙之处。

  5. Windows寄存器下溢(underflow)问题

  当子函数返回时,RETW或者RETW.N指令执行,此时也仅此时处理器将进行上溢检查。如果当Windowbase所在位置的前3个window pane(4 registers组)的WindowStart比特都为零,则意味着返回后的父函数发生过 WindowOverflow,父函数的窗口寄存器曾经被压入栈,要先行通过相应的underflow弹出。

  如果不是全为零,则应该不为零的点和正常window返回的点对应,要正常返回,如果不同,则说明发生了不正常的调用,a0被破坏掉,要产生非法指令错误。关于这个方面的具体硬件原语,读者可以参考Xtensa的ISA手册,这里不再赘述。

Alloca异常问题

  C语言中函数中经常会发生从堆栈中分配临时空间的情况,在正常的不发生窗寄存器溢出的时候没有任何问题。但是,如果该函数的下级函数的嵌套调用曾导致过寄存器溢出,由于该函数的堆栈Frame底部存有溢出的basic area的寄存器,如果简单的偏移堆栈指针来分配临时空间,则这些保存过basic寄存器会被完全破坏掉。为有效解决这一问题,Xtensa架构引入一个特殊的Alloca异常来管理basic area寄存器的搬移和临时空间的分配。

  当函数内部进行局部stack的内存分配时,Xtensa编译器会生成一个MOVSP at,as指令,异常的检测通过这一指令来完成,该指令有如下原语:

  if WindowStartWindowBase-0011WindowBase-0001 = 03 then

  Exception (AllocaCause)

  elseAR[t] ← AR[s]

  endif

  类似于underflow,如果当前寄存器窗口前3个register pane的占用状态全为0(全部为自由使用状态),则说明其上一级函数一定发生过窗口溢出,当前函数栈下方一定保存有溢出的寄存器,简单的修改SP指针不再安全,需要触发Alloca异常来进行正确处理。需要说明的是,发生alloc异常的时候,过去的寄存器窗口调用已经循环一周,且发生溢出,溢出的充分必要条件必然是当前寄存器窗口的前3个register pane占用状态全为0(WindowStartWindowBase-0011WindowBase-0001 = 000),其次当前函数不可能是调用树的叶子节点,当前函数的前半部分曾经进入过,且过去进入的路径上发生过溢出,否则就没有产生异常的必要。alloca异常是为解决sp覆盖而引入的硬件机制。

  这里解释了alloc异常产生的基本原理,那么,什么样的代码会产生MOVsp指令,从而可能触发alloc异常呢? 有如下几种情况:

  调用alloc函数,如

  void foo(int array_size) {

  char * bar = alloca(array_size);

  …

  使用变长数组(GNU C 扩展语言),如

  void foo(int array_size) {

  char bar[array_size];

  …

  使用嵌套函数定义(GNU C 扩展语言),如

  void afunction(void) {

  …

  int anotherfunction(void) {

  }

  使用特别长的局部数组,如

  void foo(void) {

  int an_array[8192]; // 32,768 bytes

  int another_array[100]; // 400 bytes

  …

  精确的size限制是32,760,包含16~48字节的Frame开销。

  当然,这里列出的不是全部的可能情况,仅仅列出几个常见用例,读者不能认为自己的代码没有以上情况,编译器就不会产生movsp指令,从而不会产生alloc异常。

  由于alloc是一种不容易避免的正常的异常,应用软件需要积极的处理。处理的思路有两种,其一是用异常或者中断栈作为临时储存来搬移,这里要介绍另外一种比较巧妙的方法,如下述代码:

  rsr a2,PS

  rsr a4,WINDOWBASE

  extui a3,a2,XCHAL_PS_OWB_SHIFT,XCHAL_PS_OWB_BITS

  xor a3,a3,a4

  slli a3,a3,XCHAL_PS_OWB_SHIFT

  xor a2,a2,a3

  wsr a2,PS

  l32i a4,a1,A4_SAVE

  l32i a3,a1,A3_SAVE

  l32i a2,a1,A2_SAVE

  addi a1,a1,USER_EXCEPTION_FRAME_SIZE

  rsync

  // Now branch on the call increment

  // We could branch earlier rotw instructions prior to the handler

  // which would avoid executing two rotw instructions in the underflow

  // eight case. However,the underflow 8 handler is right at a

  // cache-line,so that would likely involve an extra cache miss better

  // to just take the single cycle penalty here.

  rotw -1

  // what was a0 (that had the return address) is now a4

  _bbci.l a4,31,_WindowUnderflow4

  rotw -1

  // what was a0 (that had the return address) is now a8

  _bbci.l a8,30,_WindowUnderflow8

  j _setup_WindowUnderflow12

  这段代码的巧妙之处在于首先通过将当前的Windowbase保存到PS的OWB域中,然后通过反向旋转窗,根据a0的高端2bit表示的调用类型,跳转到相应的下溢exception的位置进行出栈恢复save area的寄存器,当sp指针正确偏移后,利用寄存器的引用触发overflow异常自动进行再次入栈,从而实现搬移。

  Context Switch下的寄存器保存与恢复

  在windows窗口寄存器机制下,多任务RTOS的上下文环境切换问题变得非常有趣。 那么该如何保存这些通用寄存器呢,是不是所有的物理寄存器都要保存与恢复呢? 答案是否定的,除了当前窗口的逻辑寄存器要保存外,只需要保存当前任务进程里的live寄存器(置1的寄存器pane),而恢复则只需要恢复逻辑寄存器。

  对于任务软件来说,寄存器的实现机制是透明的,因此特别要说明的是WindowsStart和WindowsBase寄存器则完全不需要保存与恢复。参考如下进程切换的环境保护核心代码:

  .Lspill_loop:

  // Top of save loop.

  // Find the size of this call and branch to the appropriate save routine.

  beqz a2,.Ldone // if no start bit remaining,we're done

  bbsi.l a2,0,.Lspill4 // if next start bit is set,it's a call4

  bbsi.l a2,1,.Lspill8 // if 2nd next bit set,it's a call8

  bbsi.l a2,2,.Lspill12 // if 3rd next bit set,it's a call12

  j .Linvalid_window // else it's an invalid window!

  // SAVE A CALL4

  .Lspill4:

  addi a3,a9,-16 // a3 gets call[i+1]'s sp - 16

  s32i a4,a3,0 // store call[i]'s a0

  s32i a5,a3,4 // store call[i]'s a1

  s32i a6,a3,8 // store call[i]'s a2

  s32i a7,a3,12 // store call[i]'s a3

  srli a6,a2,1 // move and shift the start bits

  rotw 1 // rotate the window

  j .Lspill_loop

  // SAVE A CALL8

  .Lspill8:

  ……

  该代码的基本思路是通过处理WindowStart寄存器,解析各live窗口的相对偏移,基于调用栈布局规范进行入栈处理。 当任务切换到其他的进程后,这些live窗口可能会被破坏(相应的WindowStart比特清零),这没有关系, 当进程重新切换回来时,如果这些live窗口已经在切换过的进程恢复(WindowStart比特置1),则切换回来后无需出栈,程序可正常继续执行,如果没有恢复(相应的WindowStart比特继续为零),那么该进程就可以根据这个清零的状态位将原先入栈的live寄存器正确恢复。

  本文小结

  Xtensa处理器寄存器窗口旋转函数调用是一种非常巧妙的实现机制,通过这一机制嵌入式软件可明显提高性能,并且其alloc异常,多任务上下文切换等等衍生和应用问题也可高效而经济的解决,其和TIE(Tensilica Instruction Extension),其他诸多可配置选项等等正充分说明了Xtensa架构经久不衰,广泛应用,焕发持久生命力的原因所在。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭