簡體   English   中英

來自VS 2008/2010的x86 MUL指令

[英]x86 MUL Instruction from VS 2008/2010

Visual Studio或Visual C ++ Express的現代(2008/2010)版本是否會在編譯代碼中生成x86 MUL指令(無符號乘法)? 我似乎無法找到或設想它們出現在編譯代碼中的示例,即使使用無符號類型也是如此。

如果VS不使用MUL進行編譯,是否有理由說明原因?

imul (signed)和mul (unsigned)都有一個操作數形式,它執行edx:eax = eax * src 即32x32b => 64b全乘(或64x64b => 128b)。

186添加了一個imul dest(reg), src(reg/mem), immediate形式,並且386添加了一個imul r32, r/m32形式,兩者都只計算結果的下半部分。 (根據NASM的附錄B ,另見x86標簽維基

當將兩個32位值相乘時,無論您認為值是有符號還是無符號,結果的最低有效32位都是相同的。 換句話說,只有當你查看結果的“上半部分”時,有符號和無符號乘法之間的差異才變得明顯,一個操作數imul / mul放入edx ,兩個或三個操作數imul無處放置。 因此, imul的多操作數形式可用於有符號和無符號值,並且英特爾也不需要添加新形式的mul (他們本可以使多操作數mul成為imul的同義詞,但這會使反匯編輸出與源不匹配。)

在C中,算術運算的結果與操作數具有相同的類型(在窄整數類型的整數提升之后)。 如果將兩個int相乘,則得到一個int ,而不是long long :不保留“upper half”。 因此,C編譯器只需要imul提供的東西,並且因為imulmul更容易使用,所以C編譯器使用imul來避免需要mov指令來將數據輸入/輸出eax

作為第二步,由於C編譯器使用imul的多操作數形式, imul英特爾和AMD投入了盡可能快的努力。 它只寫一個輸出寄存器,而不是e/rdx:e/rax ,因此CPU可以比單操作數形式更容易地優化它。 這使得imul更具吸引力。

在實現大數字運算時, mul / imul的單操作數形式很有用。 在C中,在32位模式下,您應該通過將unsigned long long值相乘來獲得一些mul調用。 但是,根據編譯器和操作系統,這些mul操作碼可能隱藏在某些專用功能中,因此您不一定會看到它們。 在64位模式下, long long只有64位,而不是128位,編譯器只使用imul

x86上有三種不同類型的乘法指令。 第一個是MUL reg ,它通過reg執行EAX的無符號乘法,並將(64位)結果放入EDX:EAX 第二個是IMUL reg ,它與帶符號的乘法相同。 第三種類型是IMUL reg1, reg2 (將reg1與reg2相乘並將32位結果存儲到reg1中)或IMUL reg1, reg2, imm (將reg2與imm相乘並將32位結果存儲到reg1中)。

由於在C中,兩個32位值的乘法產生32位結果,編譯器通常使用第三種類型(符號無關緊要,低32位在有符號和無符號32x32乘法之間一致)。 如果您實際使用完整的64位結果,VC ++將生成MUL / IMUL的“長乘”版本,例如:

unsigned long long prod(unsigned int a, unsigned int b)
{
  return (unsigned long long) a * b;
}

IMUL的2操作數(和3操作數)版本比單操作數版本更快,因為它們不會產生完整的64位結果。 寬乘數大而慢; 如果需要,可以更容易地構建一個較小的乘法器並使用Microcode合成長乘法。 此外,MUL / IMUL寫入兩個寄存器,這通常通過在內部將其分解為多個指令來解決 - 指令重新排序硬件更容易跟蹤每個寫入一個寄存器的兩個相關指令(大多數x86指令在內部看起來像而不是跟蹤一個寫兩個的指令。

http://gmplib.org/~tege/x86-timing.pdf ,該IMUL指令具有更低的延遲和更高的吞吐量(如果我正確讀取表)。 也許VS只是使用更快的指令(假設IMULMUL總是產生相同的輸出)。

我沒有Visual Studio方便,所以我試圖通過GCC獲得其他東西。 我也總是得到一些IMUL變化。

這個:

unsigned int func(unsigned int a, unsigned int b)
{ 
    return a * b;
}

匯總到此(使用-O2):

_func:
LFB2:
        pushq   %rbp
LCFI0:
        movq    %rsp, %rbp
LCFI1:
        movl    %esi, %eax
        imull   %edi, %eax
        movzbl  %al, %eax
        leave
        ret

在我看了這個問題之后,我在生成的代碼中找到了MULQ。

完整的代碼是將一個大的二進制數轉換為十億個塊,以便可以很容易地將其轉換為字符串。

C ++代碼:

for_each(TempVec.rbegin(), TempVec.rend(), [&](Short & Num){
    Remainder <<= 32;
    Remainder += Num;
    Num = Remainder / 1000000000;
    Remainder %= 1000000000;//equivalent to Remainder %= DecimalConvert
});

優化的生成裝配

00007FF7715B18E8  lea         r9,[rsi-4]  
00007FF7715B18EC  mov         r13,12E0BE826D694B2Fh  
00007FF7715B18F6  nop         word ptr [rax+rax] 
00007FF7715B1900  shl         r8,20h  
00007FF7715B1904  mov         eax,dword ptr [r9]  
00007FF7715B1907  add         r8,rax  
00007FF7715B190A  mov         rax,r13  
00007FF7715B190D  mul         rax,r8  
00007FF7715B1910  mov         rcx,r8  
00007FF7715B1913  sub         rcx,rdx  
00007FF7715B1916  shr         rcx,1  
00007FF7715B1919  add         rcx,rdx  
00007FF7715B191C  shr         rcx,1Dh  
00007FF7715B1920  imul        rax,rcx,3B9ACA00h  
00007FF7715B1927  sub         r8,rax  
00007FF7715B192A  mov         dword ptr [r9],ecx  
00007FF7715B192D  lea         r9,[r9-4]  
00007FF7715B1931  lea         rax,[r9+4]  
00007FF7715B1935  cmp         rax,r14  
00007FF7715B1938  jne         NumToString+0D0h (07FF7715B1900h)  

注意MUL指令5行向下。 這個生成的代碼是非常不直觀的,我知道,實際上它看起來不像編譯代碼,但是DIV非常慢〜32位div的25個周期,而現代PC上的這個圖表與MUL或IMUL相比約為75 3或4個周期)所以即使您必須添加各種額外指令,嘗試擺脫DIV也是有意義的。

我不完全理解這里的優化,但是如果你想看到使用編譯時和乘法來除常數的理性和數學解釋,請參閱本文

這是一個例子,編譯器利用完整的64乘64位未截斷乘法的性能和能力,而沒有向c ++編碼器顯示任何符號。

我的直覺告訴我,編譯器任意選擇IMUL (或兩者中較快的一個),因為無論使用無符號MUL還是有符號IMUL這些位都是相同的。 任何32位整數乘法都是64位,跨越兩個寄存器EDX:EAX 溢出進入EDX ,基本上被忽略,因為我們只關心EAX的32位結果。 使用IMUL將根據需要簽名擴展到EDX ,但我們並不關心,因為我們只對32位結果感興趣。

正如已經解釋過的那樣,C / C ++不會將word*word to double-word操作,這是mul指令最適合的。 但是有些情況下你需要word*word to double-word所以你需要擴展到C / C ++。

GCC,Clang和ICC提供了一個內置類型__int128 ,您可以使用它來間接獲取mul指令。

使用MSVC,它提供了生成mul指令的_umul128內在函數(至少自VS 2010起)。 使用此內在函數和_addcarry_u64內在函數,您可以使用MSVC構建自己的高效__int128類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM