Xtensa处理器窗寄存器函数调用机制与应用
口处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 anotherfunc
- 一种通信电源监控系统组网方案的设计(01-05)
- 通信电源监控系统下位机硬件电路的设计(01-05)
- 支持CAN总线的电动车辅助逆变电源的设计(02-23)
- 设计高性能、低成本的笔记本电脑处理器电源 (05-12)
- 微处理器电源监控芯片SGM803及其应用(06-18)
- 微控制器的市场前景及发展趋势(06-21)