`

嵌入式C语言笔记02——ARM编译器对局部变量和入口参数的处理

阅读更多
嵌入式C语言笔记02——ARM编译器对局部变量和入口参数的处理


见下面代码,显然程序的功能是将以data开始的64个整数进行累加,函数的返回值是这64个整数的累加和。表面上没有问题,但是当阅读汇编代码后就会发现问题:

	int checkSum(int *data){
	char i;
	int sum = 0;
	for(i = 0;i<64;i++)
	sum +=data[i];
	return sum;
}

下面是汇编代码:
CheckSum
MOV r2,r0
MOV r0,#0
MOV r1,#0

CheckSum_Loop
LDR r3,[r2,r1,LSL #2]
ADD r1,r1,#1
AND r1,r1,#0xff  ; Becareful this
CMP r1,#0x40
ADD r0,r3,r0
BCC CheckSum_Loop
MOV pc,r14   ;return sum

编译器在对i自加后,为什么要有一条AND指令呢?这是因为ARM处理器,不管变量的宽度是1个字节,2个字节还是4个字节都是用32位宽度的寄存器来进行存储的。对于i这个变量,虽然C语言中声明它的宽度是1个字节,但是r1寄存器的宽度确实4个字节,因此在累加后,编译器必须插入AND那条语句将r1累加后的值约束在低8位,以确保它的值小于一个8位数能表示的最大值255.
显然这条语句是多余的。
如果我们在程序中不是将i声明为char类型,而是int类型,那么编译器将不会出现这条AND语句。这样可以减少CPU消耗的时间。
再来看下面这段代码,如果将sum声明为有符号的16位short类型,那么会如何呢?

int checkSum(short *data){
	unsigned int i;
	short sum = 0;
	for(i = 0;i<64;i++)
	sum =(short)(sum+data[i]);
	return sum;
}

下面是汇编结果:
CheckSum
MOV r2,r0
MOV r0,#0
MOV r1,#0

CheckSum_Loop
ADD r3,r2,r1,LSL #1
LDRH r3,[r3,#0]
ADD r1,r1,#1
CMP r1,#0x40
ADD r0,r3,r0
MOV r0,r0,LSL #16 ;this
MOV r0,r0,ASR #16 ;and this
BCC CheckSum_Loop
MOV pc,r14   ;return sum为什么编译器要将累加后的r0寄存器先做逻辑左移16位,然后再算术右移16位呢?这是因为r0表示的是一个16位有符号数sum,编译器必须显示地保证sum在进行累加后依然是一个16位有符号数。

以上两个例子看出,对于32位系统的ARM处理器而言,即使再C程序中声明局部变量为8位或者16位,编译器依然会分配32位的寄存器用来存储。为了保证数据的值,编译器必须插入显示的代码加以处理。这样反而降低了代码的效率。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics