New*_*e18 5 x86 assembly x86-64 multiplication strength-reduction
假设%edi包含x并且我想仅使用2个连续的leal指令结束37*x,我将如何进行此操作?
例如,你可以做到45倍
leal (%edi, %edi, 8), %edi
leal (%edi, %edi, 4), %eax (to be returned)
Run Code Online (Sandbox Code Playgroud)
我不能为我的生活找出代替8和4的数字,以便结果(%eax)将是37x
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
Run Code Online (Sandbox Code Playgroud)
这是使用37 = 9*4 + 1,而不是a用第一个破坏原始值,lea所以它可以在第二个使用.
尽管如此,你最好不要发现这个:最近的clang(3.8和更新)通常会使用2个lea指令而不是imul(例如for *15),但它错过了这个并使用:
imull $37, %edi, %eax
ret
Run Code Online (Sandbox Code Playgroud)
它确实*21与gcc使用的模式相同,如5*4 + 1.(clang3.6及更早版本总是使用,imul除非有单指令替代shl或lea)
ICC和MSVC也使用imul,但他们似乎不喜欢使用2 lea条指令,所以imul那里是"故意".
有关gcc7.2与clang5.0的各种乘数,请参阅godbolt链接.有趣的是尝试gcc -m32 -mtune=pentium甚至pentium3看看gcc当时要使用多少指令.虽然P2/P3有4个循环的延迟imul r, r, i,所以这有点疯狂.Pentium有9个周期imul,没有OOO来隐藏延迟,因此努力避免它是有意义的.
mtune=silvermont应该只愿意imul用一条指令替换32位,因为它有3个周期的延迟/ 1c吞吐量倍增,但解码通常是瓶颈(根据Agner Fog,http: //agner.org/optimize/ ) .您甚至可以考虑imul $64, %edi, %eax(或其他2的幂)而不是mov/ shl,因为imul-immediate是复制和乘法.
具有讽刺意味的是,gcc错过了* 45案例和使用imul,而clang使用2 lea秒.猜猜是时候提交一些遗漏优化错误报告了. 如果 2个LEA优于1 IMUL,则应尽可能使用它们.
较旧的铿锵声(3.7岁及以上)使用imul除非一个人lea能做到这一点.我没有查看更改日志,看看他们是否做了基准来决定支持延迟而不是吞吐量.
相关:对不是地址/指针的值使用LEA?关于为什么LEA使用内存操作数语法和机器编码的规范性答案,即使它是一个shift + add指令(并且在大多数现代微体系结构中运行在ALU而不是AGU上).
| 归档时间: |
|
| 查看次数: |
1302 次 |
| 最近记录: |