把局部变量从char或者short类型转换成int类型,可以改善性能并减小代码尺寸,其实,这种转换函数类型参数也有同样的效果,看下面的例子,将2个16位的值相加,其他第2个数先减半,然后返回一个16位的和: Short add-v1(short a,short b) { Return a +(b>>1); } 虽然这个函数只是一个简单的算术运算,但是对于观察编译器碰到的问题是一个很好的例子,输入值a,b和返回值都存放在32位的ARM寄存器中,编译器是否应该考虑这些32位数值在short类型的范围之间呢?或者编译器是否应该通过对低16位数据进行符号位扩展,充填32位寄存器,强制把数值限制在上述范围之间呢?编译器必须把调用者和被调用者作出一致的决策,不是调用者,就是被调用者,必须把数据转换为short类型。 如果参数可以不缩小到定义的数据类型范围,那么称这种函数参数传递是宽的,反之,则称为窄的。观察add-v1的汇编输出结果,就可以知道编译器所来用的是那种形式,如果编译器传递参数是宽的。那么被调用者就必须把参数缩小到正确的用的是哪种形式。如果编译器传递参数是窄的。那么调用者必须缩小参数范围。如果编译器返回值是窄的。那么被调用者就必须在返回前缩小回值的范围。 对于ADS的armcc来说,函数参数传递和返回值都是窄的。换句话说,调用者要处理调用参数,而被调用者要处理返回值,编译器采用函数的ANSI原型来决定函数参数的数据类型。 函数add-v1在armcc下的输出结果显示,编译器把返回值的类型转换为short类型的范围之内,这就说明参数传递和返回值都是窄的。 Add-v1 Add r0,r0,r1,ASR #1 ;r0=(int)a+((int) b>>1) MOV r0,r0,LSL #16 MOV r0,r0,ASR #16 ;r0=(short) r0 MOV pc,r14 ;return r0 Gcc编译器更为谨慎,对参数值的范围不作任何假设,这个版本的编译器,在调用者和被调用者中将输入参数缩小到short类型的数据范围,同时也都将返回值转换为short类型,下面是add-v1由gcc编译后的代码: Add-v1-gcc MOV r0,r0,LSL #16 MOV r1,r1,LSL #16 MOV r1,r1,LSL #17 ;r1=(int)b>>1 ADD r1,r1,r0,ASR #16 ;r1+=(int)a MOV r1,r1,LSL #16 MOV r0,r1,ASR #16 ;r0(short)r1 MOV pc,1r ;return r0 尽管宽和窄的函数调用规则各有其优点,但是char或者short类型的函数参数和返回值都会产生额外的开销,导致性能的下降,并增加了代码尺寸。所以,即使是传输一个8位的数据,函数参数和返回值使用int类型也会更有效。 |