系统
当前位置: 主页 > 杂文详谈 > 系统
对于加法运算的优化
发布日期:2022-10-26 阅读次数:

编译器对于加法运算的优化

优化常见的几种方式

常量传播

将编译期间可计算出结果的变量转换为常量,减少变量的使用

#include 
int main()
{
    int nVar = 1;
    printf("nVar = %d \n",nVar);
}

对于上述代码,如果没有进行优化,那么处理的逻辑为:

WeChat Screenshot_20221012100902.png

会先将该值放入栈中,在 printf要输出它时,再将其拿到 esi中,通过 rsi传给 printf进行输出

进行 O1 优化以后

WeChat Screenshot_20221012101047.png

由于变量 nVar是一个在编译期间可以计算出结构的变量,所以将其替换为常量,直接用 1 来替代,printf要使用时直接传入 1 即可

O2 优化中在对变量的处理一致

WeChat Screenshot_20221012101142.png

常量折叠

在公式中出现多个常量进行计算的情况中,且编译器可以算出结果的情况下,在编译期间直接用计算结果替代

#include 

int main()
{
    int nNum = 1 + 8 * 7 - 10;
    printf("nNum = %d \n",nNum);

}

对于上述代码,如果没有进行优化,那么处理的逻辑为:

WeChat Screenshot_20221012102428.png

但其实这也进行了一定的优化,没有将常量的计算生成对应的计算指令,而是编译器直接将计算的结果传入栈中,接下来再从栈中拿值

进行 O1 优化以后

WeChat Screenshot_20221012103012.png

此时在上面的基础上,没有将结果放入栈中,由于后面 printf要使用该参数,直接传入 edx,后面通过 rdx直接传入 printf

复写传播

#include 

int main(int argc)
{
    int nNum = argc;
    printf("nNum = %d \n",nNum);
}

此时我们将 main函数的参数值赋给了 main中的变量 nNum

对于上述代码,如果没有进行优化,那么处理的逻辑为:

WeChat Screenshot_20221012111407.png

可以看到,为了将 edi中的的参数 1 (argc)放入到栈 nNum的位置,进行了很繁琐的操作

进行 O1 优化以后

WeChat Screenshot_20221012111747.png

直接删除掉了 nNum(因为之后没有对 nNum重新赋值) 用存在于 edi中刚刚传入的 argc值直接代替他就完事了

WeChat Screenshot_20221012111131.png
后面调用 printf时,由于其已经被传入了 edi,若直接通过 rdi传给 printf就完事了,非常的简便

具体在代码中的优化

#include 
#include 

int main()
{
    int nVarOne = 0;
    int nVarTwo = 0;

    nVarOne = nVarOne + 1;

    nVarOne = 1 + 2;

    nVarOne = nVarOne + nVarTwo;

    printf("nVarOne = %d \n",nVarOne);

    return 0;
}

对于上述代码,如果没有进行优化,那么处理的逻辑为:

WeChat Screenshot_20221012093319.png

但还是在编译期间提前做了进行了简单的将常量的计算

进行 O1 优化以后

WeChat Screenshot_20221012104338.png

代码结构发生大变,处理的流程为:

  1. 常量替换变量,所以 nVarOne``nVarTwo开始都被直接替换为常量 0

  2. 接下来代码中对 nVarOne做了两次赋值操作,由于第二次赋值操作会覆盖第一次赋值的操作,所以直接去掉第一次赋值,第二次赋值为 3

  3. 再次用常量替换变量,由于 printf会使用到该参数,直接把 3 传入 edx,等待后续通过 rdx传入 printf即可

所以最终反映到汇编指令中,只有一条 mov edx,0x3实属省事啊。。。

本文转载自FreeBuf.COM