MSVS C++编译器优化的理解

10

我不理解这段代码在做什么。C代码如下:

#include <stdio.h>

int main()
{
    const int mul = 100;
    int x;
    printf_s("Input a number\r\n");
    scanf_s("%i", &x);
    printf_s("%i/%i = %i\r\n", x, mul, x / mul);
    return 0;
}

我原本期望生成的汇编代码只会有一些简单的移位和加减操作,但是却出现了一些魔法常量,例如51EB851Fh,以及乘法等。这是怎么回事呢?

; int __cdecl main()
_main proc near

x= dword ptr -8
var_4= dword ptr -4

push    ebp
mov     ebp, esp
sub     esp, 8
mov     eax, ___security_cookie
xor     eax, ebp
mov     [ebp+var_4], eax
push    offset Format   ; "Input a number\r\n"
call    ds:__imp__printf_s
lea     eax, [ebp+x]
push    eax
push    offset aI       ; "%i"
call    ds:__imp__scanf_s
mov     ecx, [ebp+x]
mov     eax, 51EB851Fh
imul    ecx
sar     edx, 5
mov     eax, edx
shr     eax, 1Fh
add     eax, edx
push    eax
push    64h
push    ecx
push    offset aIII     ; "%i/%i = %i\r\n"
call    ds:__imp__printf_s
mov     ecx, [ebp+var_4]
add     esp, 1Ch
xor     ecx, ebp        ; cookie
xor     eax, eax
call    @__security_check_cookie@4 ; __security_check_cookie(x)
mov     esp, ebp
pop     ebp
retn
_main endp

你是如何得到生成的汇编代码的? - Yamen Ajjour
1
ida,但你可以使用“免费反编译器”来谷歌任何免费实用工具。 - Alex Zhukovskiy
2个回答

13
处理器不太擅长除法,一个idiv可能需要11到18个周期。与移位和乘法相反,它们通常只需要一个周期。
因此优化器使用定点数学将您的除法替换为乘法,利用32位乘法产生edx:eax的64位结果。简略计算:n / 100 == n * 0.32 / 32 == n * (0.32 * pow(2,32)) / 32 / pow(2,32)。这些除法非常便宜,只需要右移。乘法器变成了0.32 * pow(2,32) ~= 1374389535 == 0x51EB851F。

根据英特尔规格,imul需要3个周期,你的意思是它只需要1个周期还是仅指加法? - Alex Zhukovskiy
3
使用Agner Fog的指令表,可以按照体系结构和寻址模式来分解它。在Sandy Bridge架构上,需要1个周期完成。 - Hans Passant

0

1
你只是在陈述他已经知道的东西。他想知道为什么这样做有效。 - deviantfan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接