簡體   English   中英

如何在x86中僅使用2個連續的leal指令將寄存器乘以37?

[英]How to multiply a register by 37 using only 2 consecutive leal instructions in x86?

假設%edi包含x並且我想僅使用2個連續的leal指令結束37 * x,我將如何進行此操作?

例如,你可以做到45倍

leal (%edi, %edi, 8), %edi   
leal (%edi, %edi, 4), %eax (to be returned)

我不能為我的生活找出代替8和4的數字,以便結果(%eax)將是37x

-O3 ,gcc將發出(Godbolt編譯器瀏覽器)

int mul37(int a)  { return a*37; }

    leal    (%rdi,%rdi,8), %eax      # eax = a * 9
    leal    (%rdi,%rax,4), %eax      # eax = a + 4*(a*9)
    ret

那是使用37 = 9*4 + 1而不是用第一個lea破壞原來a值,所以它可以在第二個中使用它們。

盡管如此,你最好不要發現這個:最近的clang(3.8和更新)通常會使用2個lea指令而不是imul (例如*15 ),但它錯過了這個並使用:

    imull   $37, %edi, %eax
    ret

它使用與gcc使用的相同模式執行*21 ,為5*4 + 1 (clang3.6及更早版本總是使用imul除非有單指令替代shllea

ICC和MSVC也使用imul,但他們似乎不喜歡使用2個lea指令,因此imul是“故意”的。

有關gcc7.2與clang5.0的各種乘數,請參閱godbolt鏈接。 有趣的是嘗試使用gcc -m32 -mtune=pentium甚至是pentium3來查看gcc當時要使用多少指令。 雖然P2 / P3對於imul r, r, i有4個周期的延遲,所以這有點瘋狂。 Pentium有9個周期imul ,沒有OOO來隱藏延遲,因此努力避免它是有意義的。

mtune=silvermont應該只願意用一條指令替換32位imul ,因為它有3個周期的延遲/ 1c吞吐量倍增,但解碼通常是瓶頸(根據Agner Fog, http: //agner.org / optimize / )。 你甚至可以考慮imul $64, %edi, %eax (或其他2的冪)而不是mov / shl ,因為imul-immediate是一個復制和乘法。


具有諷刺意味的是, gcc錯過了* 45案例,並使用了imul ,而clang使用了2個lea s。 猜猜是時候提交一些遺漏優化錯誤報告了。 如果 2個LEA優於1 IMUL,則應盡可能使用它們。

較老的鏗鏘聲(3.7及以上)使用imul除非單個lea能夠做到這一點。 我沒有查看更改日志,看看他們是否做了基准來決定支持延遲而不是吞吐量。


相關: 對不是地址/指針的值使用LEA? 關於為什么LEA使用內存操作數語法和機器編碼的規范性答案,即使它是一個shift + add指令(並且在大多數現代微體系結構中運行在ALU而不是AGU上)。

暫無
暫無

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

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