第三篇:GCC中对寄存器的分配与使用
在很多用于AVR的RTOS中,都会有任务调度时,插入以下的语句:
入栈:
__asm____volatile__("PUSHR0
\t");
__asm____volatile__("PUSHR1
\t");
......
__asm____volatile__("PUSHR31
\t");
出栈
__asm____volatile__("POPR31
\t");
......
__asm____volatile__("POPR1
\t");
__asm____volatile__("POPR0
\t");
通常大家都会认为,在任务调度开始时,当然要将所有的通用寄存器都保存,并且还应该保存程序状态寄存器SREG。然后再根据相反的次序,将新任务的寄存器的内容恢复。
但是,事实真的是这样吗?如果大家看过陈明计先生写的smallrots51,就会发现,它所保存的通用寄存器不过是4组通用寄存器中的1组。
在WinAVR中的帮助文件avr-libcManual中的RelatedPages中的FrequentlyAskedQuestions,其实有一个问题是"WhatregistersareusedbytheCcompiler?"回答了编译器所需要占用的寄存器。一般情况下,编译器会先用到以下寄存器
1Call-usedregisters(r18-r27,r30-r31):调用函数时作为参数传递,也就是用得最多的寄存器。
2Call-savedregisters(r2-r17,r28-r29):调用函数时作为结果传递,当中的r28和r29可能会被作为指向堆栈上的变量的指针。
3Fixedregisters(r0,r1):固定作用。r0用于存放临时数据,r1用于存放0。
还有另一个问题是"Howtopermanentlybindavariabletoaregister?",是将变量绑定到通用寄存器的方法。而且我发现,如果将某个寄存器定义为变量,编译器就会不将该寄存器分配作其它用途。这对RTOS是很重要的。
在"InlineAsm"中的"CNamesUsedinAssemblerCode"明确表示,如果将太多的通用寄存器定义为变量,刚在编译的过程中,被定义的变量依然可能被编译器占用。
大家可以比较以下两个例子,看看编译器产生的代码:(在*.lst文件中)
第一个例子:没有定义通用寄存器为变量
#include<avr/io.h>
unsignedcharadd(unsignedcharb,unsignedcharc,unsignedchard)
{
returnb+c*d;
}
intmain(void)
{
unsignedchara=0;
while(1)
{
a++;
PORTB=add(a,a,a);
}
}
在本例中,"add(a,a,a);"被编译如下:
movr20,r28
movr22,r28
movr24,r28
rcalladd
第二个例子:定义通用寄存器为变量
#include<avr/io.h>
unsignedcharadd(unsignedcharb,unsignedcharc,unsignedchard)
{
returnb+c*d;
}
registerunsignedcharaasm("r20");//将r20定义为变量a
intmain(void)
{
while(1)
{
a++;
PORTB=add(a,a,a);
}
}
在本例中,"add(a,a,a);"被编译如下:
movr22,r20
movr24,r20
rcalladd
当然,在上面两个例子中,有部份代码被编译器优化了。
通过反复测试,发现编译器一般使用如下寄存器:
第1类寄存器,第2类寄存器的r28,r29,第3类寄存器
如在中断函数中有调用基它函数,刚会在进入中断后,固定地将第1类寄存器和第3类寄存器入栈,在退出中断又将它们出栈。