电脑疯子技术论坛|电脑极客社区

微信扫一扫 分享朋友圈

已有 418 人浏览分享

对于乘法运算的优化 | GCC

[复制链接]
418 0


主要通过观察反汇编代码来观测编译器对代码的优化策略,主要观察默认情况下的优化
策略与-O1编译选项下的优化策略

乘法指令对应的汇编指令为:

有符号乘法imul

无符号乘法mul

乘法指令执行周期过长,编译器会首先通过移位配合加法、减法来完成 当使用这些指
令都无法完成时,才会使用乘法指令

实例代码

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char** argv)
{

        int nVarOne = argc;
        int nVarTwo = argc;

        printf("nVarOne * 15 = %d",nVarOne * 15);

        printf("nVarOne * 16 = %d",nVarOne * 16);

        printf("nVarOne * 4 + 5 = %d",nVarOne * 4 + 5);

        printf("nVarOne * nVarTwo = %d",nVarOne * nVarTwo);

        return 0;
}

逐行观察

无优化选项

基础变量的赋值

QQ截图20221110141626.png

如图所示,在不优化的前提下,会先将argc与argv(地址) 分别放入栈中,之后再依据代码
将argc分别放在nVarOne和nVarTwo变量所对应的地址中

nVarOne : $rbp - 0x8

nVarTwo : %rbp - 0x14

1:printf("nVarOne * 15 = %d",nVarOne * 15);

20.png

nVarOne * 15的操作在红色方框中实现,编译器并没有直接使用乘法指令,而是借助了16 - 1 = 15
这个简单的机制,进行了一定的优化:

mov edx,DWORD PTR [rbp-0x8]将nVarOne的值(也就是1) 放入rdx寄存器低位中
mov eax,edx再将1放入rax寄存器的低位中
shl eax,0x4关键操作:使用左移运算将乘法拆分,左移 4 位,此时eax = 16
sub eax,edx由于edx中的值为1,所以完成16 - 1 = 15的运算,这也正是我们nVarOne * 15的结果
mov esi,eax将结果放入esi,用于后续传给printf即可

2:printf("nVarOne * 16 = %d",nVarOne * 16);

19.png

有了上一个优化的方法,这个就非常简单了,直接 左移 4 位即可

shl eax,0x4左移4位eax = 16

3:printf("nVarOne * 4 + 5 = %d",nVarOne * 4 + 5);

18.png

方式同上:将nVarOne * 4 + 5转换为nVarOne << 2 + 5

shl eax,0x2add eax,0x5

4:printf("nVarOne * nVarTwo = %d",nVarOne * nVarTwo);

17.png

此时由于nVarOne与nVarTwo均为未知变量,而不像上述几种情况中带有常量,所以在非 O1优化的情
况下直接使用imul有符号乘法进行相乘

O1优化选项

在 O1 优化的情况下,没有给nVarOne与nVarTwo分配空间,而是直接将参数作为局部变量来使用所以
甚至没有设置rbp的值,唯一的开辟的 8 字节空间只是为了保存之前rbp的值

1:printf("nVarOne * 15 = %d",nVarOne * 15);

16.png

整体的思路还是1 << 4 - 1但此时没有使用栈空间,而是直接将argc的值放入ebp中 (mov ebp , edi)
再直接对ebp进行移位与减法的操作,最终将其放入rdx进行输出即可

15.png

2:printf("nVarOne * 16 = %d",nVarOne * 16);

14.png

此处直接传递ebp中的值,由于ebp之前的值未1 << 4,所以此处直接输出即可

3:printf("nVarOne * 4 + 5 = %d",nVarOne * 4 + 5);

13.png

之前将edi的值传递了两次,一个份放在了ebp中,一份放在了ebx中,此处的这种优化方式就比较有意思了区别于之前的
先移位在做加法,此处直接借用lea指令完成计算,当这种组合运算中的乘数≠ 2、4、8时,编译器首先会尝试将乘数拆
分为2、4、8的一种再配合其余运算,若无法拆分则使用imul计算或其余优化方式

nVarOne * 9 + 5

12.png

4:printf("nVarOne * nVarTwo = %d",nVarOne * nVarTwo);

11.png

此处依旧是使用imul无符号乘法进行运算

您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

关注

0

粉丝

9021

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Powered by Pcgho! X3.4

© 2008-2022 Pcgho Inc.