CSAPP–第三章-程序的机器级表示(下)

Aki 发布于 2022-10-28 375 次阅读


【CSAPP-深入理解计算机系统】3-5.指令与条件码_哔哩哔哩_bilibili

【CSAPP-深入理解计算机系统】3-6.跳转指令与循环_哔哩哔哩_bilibili

程序的机器级表示_晨哥是个好演员的博客-CSDN博客_程序的机器级表示

leaq指令、

加载有效地址(load effective address)指令leaq实际上是movq指令的变形,它的指令形式是从将内存地址读写到到寄存器当中,但实际根本未引用内存,也没有从内存地址处读取数据,它的第一个操作数看上去是内存引用,但该指令并不是从指定位置读入数据,而是将有效地址写入到目的操作数。

以上为leaq指令将有效地址值写入寄存器中的过程,即加载有效地址。leaq不仅可以加载有效地址,还可表示加法和有限乘法运算,对下述代码进行编译:

汇编得到的汇编代码指令如下:

 leaq    (%rdi,%rsi,4), %rax
 leaq    (%rdx,%rdx,2), %rdx
 leaq    (%rax,%rdx,4), %rax
 ret

x存放在rdi寄存器中,y存放在rsi寄存器中,z存放在rdx寄存器中,那么第一条指令将rdi+4*rsi放入rax中,即表示x+4y;第二条指令表示将3z写入rdx寄存器;第三条指令则是将rax存放的x+4y与4rdx相加结果写入rax,此时rax存放的是x+4y+12z的结果,最终rax寄存器作为返回值返回即可。
leaq指令能执行加法和有限的乘法,在编译如上简单的算术表达式时,是很有用处的。

一元和二元操作、移位操作、特殊算术操作、

移位操作:

我们根据具体代码举例说明移位操作,示例如下:

因为w是两个字节,16位,即2的4次方为16。

我们分析一下这个代码第3行,对应的汇编指令:

上图rdx的值为z,rax通过第一条指令,存放3z的值,然后第二条指令左移4位等同于乘以2的4次方即16,所以这两条指令最终计算的是48z的值,存放至rax寄存器中。为什么编译器不直接使用乘法指令来实现这个运算呢?主要是因为乘法指令的执行需要更长的时间,因此编译器在生成汇编指令时,会优先考虑更高效的方式。

条件码、

条件码寄存器其实也称为标志寄存器,其具有三种作用:

  • 用来存储相关指令的某些执行结果;
  • 用来为CPU执行相关指令提供行为依据;
  • 用来控制CPU的相关工作方式。

以下是8086相关标志位:

条件码寄存器(状态寄存器)的值是由ALU在执行算术和运算指令时写入的,下图中的这些算术和逻辑运算指令都会改变条件码寄存器的内容:

对于不同的指令也定义了相应的规则来设置条件码寄存器。例如:

  • 逻辑操作指令xor,**进位标志(CF)溢出标志(OF)**会置0
  • 对于inc加一指令和dec减一指令会设置溢出标志(OF)和零标志(ZF),但不会改变进位标志(CF)。

关于条件码使用,我们举例看下如下代码以及其汇编指令:

gcc -Og -S a.c得到汇编指令为:

comp:
        cmpq    %rsi, %rdi  // a-b
        sete    %al         //根据标志设置寄存器al的值
        movzbl  %al, %eax   //把寄存器al的值复制到寄存器eax上,并进行零扩展
        ret

上面已经介绍了cmp指令,cmp指令是根据两个操作数的差来设置条件码寄存器,如果计算得到的结果为0,则设置ZF为1,否则为0。cmp指令和减法指令sub类似,也是根据两个操作是的差来设置条件码,二者不同的是cmp指令只是设置条件码寄存器,并不会更新目的寄存器的值

在这个例子中,指令sete根据需标志(ZF)的值对寄存器al进行赋值,后缀eequal的缩写。如果零标志等于1,指令sete将寄存器al置为1;如果零标志等于0,指令sete将寄存器al置为0

然后mov指令对寄存器al进行零扩展,最后返回判断结果,存放至rax寄存器中。

下面看一个复杂例子,代码如下:

转成汇编指令后:

异或是相同为0,不相同为1!!!不溢出设置OF为0,溢出设置OF为1。

这里是8位的有符号数,数值范围是-128~+127 。

溢出的判断方法有三种:

1.由计算结果来判断

当出现:“正+正=负、负+负=正、正-负=负、负-正=正”的时候,都是产生了溢出。

2.由计算过程中的进位来判断

当出现:最高位的进位(即符号位的进位)和“次高位”的进位 不相同 的时候,就是产生了溢出。

3.由计算后的标志位来判断

当出现:OF = 1 时,就是产生了溢出。

判断a<b是否为真,需要首先判断a-b的值,a-b<0设置SF=1,反之SF=0,然后判断是否正溢出或负溢出,溢出置OF=1,反之OF=0;计算SF^OF,若结果为1,则a<btrue;反之a<bfalse。所以,综上可发现,根据符号标志(SF)和溢出标志(OF)的异或结果,可以对a小于b是否为真做出判断。更多相关set指令如下: