(13)优化内存访问:
内存访问是处理器执行的最慢功能之一。编写高性能的汇编语言程序时,最好尽可能的避免内存访问。目前大多数处理器使用了数据缓存,但是另外一个要考虑的问题是处理器如何处理内存的读取和写入。大多数处理器被优化为从数据段的开始位置,在特定的缓存块中读取和写入内存位置。
在奔腾4处理器中,缓存块的长度是64位,如果定义的数据元素超过了64位块的边界,就必须使用两次缓存操作才能获取或存储内存中的数据元素。
为了解决这个问题:
《》按照16字节的边界对准16位数据
《》对准32位数据使它的基址是4的倍数。
《》对准64位数据使它的基址是8的倍数。
《》避免在堆栈中使用大的数据长度。
.align命令紧贴在数据定义前面,例如:
.section .data
.align 16 #对齐16字节的边界
Output:
.int 32
(14)在函数调用时,被调用的函数内,首先pushl %ebp,然后movl %esp, %ebp。在执行完之后,movl %ebp, %esp,最后popl %ebp。如果函数是带有参数的调用的话,8(%ebp)内存位置就是第一个参数,为什么要加上8呢,因为ebp指向的是堆栈中前一个ebp的值,4(%ebp)指向的是返回的地址。可以用一个图表示出来。
函数返回地址 | ||
EBP的值 | ||
被调用函数的返回值 | ||
函数内部变量1 | ||
函数内部变量2 | ||
…………………… | ||
调用其他函数时的传递参数1 | ||
调用其他函数时的传递参数2 | ||
…………………… | ||
|
在进入被调函数的时候,首先要保存ebp的值,然后用ebp寄存器保存当前esp的值,因为函数执行过程中可能要改变esp的值,保存的目的是为了能够找到堆栈中的ebp和返回地址。在被调函数返回之前,也就是ret指令之前,要恢复esp的值,然后恢复ebp的值,这时的ebp的值保存的是调用函数的原始esp值。也就是说ebp寄存器充当了寻找函数参数的指针和堆栈基指针的作用。
上图所示:如果变量1和变量2的话正好是传递的参数的话,就不能使用push和pop了,因为变量已经在堆栈上了,gcc编译器使用的是movl指令,也就是说用movl指令造了函数调用堆栈结构。
在这里不得不提到两个cpu指令,说是cpu的指令,但是也可能是用汇编封装起来的操作指令的组合,也可能是硬件电路层面的操作组合。
enter 和 leave: leave指令相当于 movl %ebp, %esp , popl %ebp
enter指令相当于: pushl %ebp movl %esp, %ebp
(15)条件传送指令和跳转指令中需要注意的是:对于计算无符号整数时,跳转指令使用above和 below关键字。对于带符号整数值,使用greater和less关键字。
(16)零标志位:表示两数相等。
溢出标志位:专门用在处理带符号数字时,表示带符号的值超出了范围。例如:
movl $0x7f,%bl addb $10,bl
这就会设置溢出标志位,因为127+10=137,超出了带符号的范围-128—127。但是进位标志位不会设置,因为在无符号值的范围内0-255 。就是利用了溢出标志位和进位标志位的分别检测,来定义认为我们操作的数是有符号的还是无符号的。也就是说,在汇编的代码中,我们无法表示一个值是有符号的还是无符号的,在内存中都是二进制的,通过运算中,检查溢出标志位和进位标志位配合跳转指令达到我们把一个值当做是有符号的还是无符号的。例如,我们检测到了溢出标志位,然后配合跳转指令,到达了我们要采取的操作,此时这个值被我们当做了有符号的对待了。相反,则是当做了无符号的对待。溢出标志位和进位标志位永远不可能同时被设置。
进位标志位:表示无符号数中何时发生溢出。当指令导致寄存器超出了其数据长度的限制时设置进位标志。注意:inc和dec CPU指令不影响进位标志,只影响溢出标志,也就是说在dec和inc指令后面的数据会被当做是有符号数对待。和其他指令不同,有专门可以修改进位标志的指令:
CLC:清空进位标志(0)
CMC:对进位标志求反
STC:设置进位标志(1)
(17)loop循环指令(CPU指令)的好处在于他们递减ecx寄存器的值时,不影响eflags寄存器的标志位。当ecx寄存器的值到达0时,零标志位也不会被设置。
(18)在这里特别讲一下寄存器的溢出和进位:
考虑一个问题,在loop循环中,如果ecx寄存器开始就是0的话,会不会一直循环呢,到哪里结束循环呢?
Loop指令的逻辑是先把ecx寄存器的值减1,然后再判断ecx寄存器的值是不是为0 。如果ecx开始的时候就是0呢,loop先把ecx减去1,变成了-1,loop检测到ecx不为0,再次循环。然后一直循环到了ecx寄存器中的值变成了最,小的负数(100000……二进制的形式),再减去1的话。溢出标志位被设置,但是,这个时候ecx中的值还不为0,为0111111……的二进制形式,再减去1,一直循环到了ecx为0了,这个时候循环停止了,这个时候循环的次数已经不是我们预期需要的了。如果ecx为0的话,再减去1的话,进位标志就要被设置了。过程就是这样的逻辑,还有一个需要注意的就是溢出标志位可能是硬件自动清除的,进位标志由于提供了操作的指令clc cmc stc,猜测是软件清除的。