• 算术与逻辑运算指令具体解释


    算术与逻辑运算指令具体解释

    前言

    上一次解说了数据传送指令,香型大家肯定对此有了一定的认识了.这些简单的汇编指令,却能够将复杂的程序井然有序的运行完成,实在是让人惊叹.

    算术与逻辑运算指令

    算术逻辑运算包含非常多种,各级大家应该能非常快想出来,比方常见的加减乘除,与或非,左移右移等等另一个区地址运算符,大家可能想不到,可是看完以下这一部分之后,就会认为这个取地址运算符是个精妙的指令.

    先说一下各个指令,见下图:

     

    这里面比較特别的指令就是leal(取地址指令),其余的指令都是比較常规的算术和逻辑运算,相比之下非常好理解,因此在这里咱们重点是介绍leal指令.

    leal指令

    leal指令时很奇妙的一个指令,他能够去一个存储器操作数的地址,而且将其赋给目的操作数.假设用C语言其中来相应的话,就相当于&运算符.

    比方对于

    leal 4(%edx,%edx,4),%eax

    这条指令来说,我们如果%edx寄存器的值为x的话,那么这条指令的作用就是将4+x+4x=5x+4赋给%eax寄存器.他和mov指令的差别就在于,如果是
    movl 4(%edx,%edx,4),%eax

    这条指令,它的作用是将内存地址为5x+4的内村区域的值赋给%eax寄存器,leal指令仅仅是将5x+4这个地址赋给目的操作数%eax而已,它并不正确寄存器进行引用的值的计算.

    为了更好的表示这条指令的效果,咱们用一个图来简单的表示这一过程.我们如果下图是运行指令之前,寄存器和存储器的状态.

     

    能够看到,此时在寄存器中,地址为5x+4的区域的值为1000.那么此时若是进行

    movl 4(%edx,%edx,4),%eax

    操作,非常显然,%eax的值应该为1000,也就是下图:

     

    可是假设进行

    leal 4(%edx,%edx,4),%eax

    操作的话,%eax的值就不是1000,由于leal指令不回去取存储器其中的值,因此寄存器%eax的值应该是5x+4.

     

    试想一下,倘若在地址为5x+4的位置存储的是变量i,那么事实上这条指令就相当于&i操作,也就是C语言其中的&取地址操作的汇编级做法.

    一个案例:

    因为其它的指令非常easy,因此咱们就不一一介绍了,咱们使用一个小程序来做一个实例,顺道看一下上面的算术与逻辑运算指令都是被怎样使用的.我们考虑这样一个C语言程序:

    int arith(int x, int y , int z){
        int t1 = x+y;
        int t2 = z*48;
        int t3 = t1&0xFFFF;
        int t4 = t2*t3;
        return t4;
    }


    这里面包括了加,,与运算,我们使用

    GCC -O1 -S sum.c

    这条命令,然后使用
    cat sum.c

    这条命令查看,就会得到例如以下的汇编代码:

    .file    "sum.c"
        .text
    .globl arith
        .type    arith, @function
    arith:
        pushl    %ebp
        movl    %esp, %ebp
      //以上为栈帧建立
        movl    16(%ebp), %eax
        leal    (%eax,%eax,2), %edx
        sall    $4, %edx
        movl    12(%ebp), %eax
        addl    8(%ebp), %eax
        andl    $65535, %eax
        imull    %edx, %eax
      //下面为栈帧完毕
        popl    %ebp
        ret
        .size    arith, .-arith
        .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
        .section    .note.GNU-stack,"",@progbits


    这里面还有leal指令,能够看到程序其中并没有取地址&操作,所以这里的leal指令不是用来取地址的,咱们使用一个图来演示这个程序的执行过程.首先是栈帧的建立过程,栈帧建立好以后,寄存器和存储器的状态例如以下:

     

    以上便是建立好的栈帧,通上一次一样,栈指针和帧指针都指向了一个新的位置,在帧指针偏移量为8,,12,16的地方存储着传递进来的參数x,y,z.接下来我们就開始分析,在汇编代码层次,是怎样完毕上述C语言程序其中的一些列动作的.

    首先是一个mov指令,他的作用非常easy,就是将參数z取入寄存器,以下是它的汇编代码以及图示:

    movl 16(%ebp),%eax


     

    上面的指令比較简单,接下来的这条指令就有点特别了,是一条leal指令.这里的leal指令不是用来取地址的,而是用来进行乘法运算的,他的目的是将%eax寄存器其中的值乘以3,然后发送至%edx寄存器.而採用的方式则是2*x+x的方式,这正是我们之前讲过的乘法优化算法,使用移位和加法来计算乘法.

    leal (%eax,%eax,2),%edx


     

    上面计算3z的目的,在接下来的这一条指令就看出来了.接下来的一条会令是sal左移操作,位数为4,左移4位事实上就相当于16,因此接下来的一条指令事实上就相当于将寄存器%edx当值的值乘以16,这事实上刚好是在计算48*z.从这里也能够看出来,在运行C程序的时候,并不一定依照程序其中的顺序去计算.下面是sal指令的内容与图示

    sall $4,%edx


     

    接下来的指令依旧是简单的取參数y,因此咱们就不解释了,看指令和图示

    movl 12(%ebp),%eax


     

    以下的一条指令是add加法指令,它是将左边操作数的值加到右边的目的操作数.也就是将内存地址为8(%ebp)的值加到%eax寄存器,8(%ebp)这个位置存放的刚好是x,因此这里计算的便是x+y的值,而结果会存入%eax寄存器.以下是指令的内容和图示:

    addl 8(%ebp),%eax


     

    接下来是一条运算指令and,他计算的则是t10xFFFF(十进制就是65535)的与运算,t1的值为x+y,此时就存在%eax寄存器.接下来看这条指令和图示:

    andl $65535 ,%eax


    接下来是最后一个计算过程的指令imul乘法指令,它的作用也是将左边操作数的值乘到右边的目的操作数上.也就是将%edx寄存器的值乘到%eax寄存器上面去,%edx此时的值为48*z(也就是t2),%eax的值为(x+y)&0xFFFF(也就是t3),两者相乘得到t4的值,结果将存放在%eax寄存器,而且作为返回值返回,下面是指令和图示:

    imull %edx,%eax


    至此,我们整个计算过程就结束了,当中用到了一些算术与逻辑运算指令,事实上他们并没有什么难度,图例已经说得非常清楚了.最后则是栈帧的完毕部分,下面为当前帧释放后的状态:

     

    不知道打击注意到了没有,每次在%ebp偏移量为4的位置都是空着的,而參数都在8,12,16这种位置,难道偏移量为4的位置是空的吗?

    当然不是,地址不会跳着来,事实上他存储的是返回地址,这一点在此不做过多的说明,以后遇到了再具体的说明.

    小小的结一下

    本章内容是认识一下一些常见的算术和逻辑运算指令.事实上没有什么难度.

  • 相关阅读:
    C++中的异常
    Hadoop YARN介绍
    js处理层级数据结构的一些总结
    Python数据结构
    Python的编码风格
    Python流程控制
    java中面试可能会问的问题
    深度学习
    Pescal Triangle Two
    Pascal Triangle
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7272807.html
Copyright © 2020-2023  润新知