Assembly 如何复制寄存器并执行'x*4+;常数`具有最少的指令数

Assembly 如何复制寄存器并执行'x*4+;常数`具有最少的指令数,assembly,x86,micro-optimization,Assembly,X86,Micro Optimization,我是x86汇编的新手。例如,对于以下指令:将ESP的内容乘以4并加上0x11233344,将结果存储在EDI中 如何在x86程序集中以最少的指令数表示此指令 push esp mov edi, 4 mul edi add edi, 0x11233344 您的asm没有任何意义(push espcopies to memory,而不是另一个寄存器),并且mul-edi写入EDX:EAX notedi。它不包含EDX:EAX=EAX*src\u操作数。请阅读手册:。或者更好地使用imul,除非您实

我是x86汇编的新手。例如,对于以下指令:将ESP的内容乘以4并加上0x11233344,将结果存储在EDI中

如何在x86程序集中以最少的指令数表示此指令

push esp
mov edi, 4
mul edi
add edi, 0x11233344

您的asm没有任何意义(
push esp
copies to memory,而不是另一个寄存器),并且
mul-edi
写入EDX:EAX not
edi
。它不包含EDX:EAX=EAX*src\u操作数。请阅读手册:。或者更好地使用
imul
,除非您实际需要全32x32=>64位乘法的高半输出

另外,不要使用堆栈指针寄存器ESP来保存临时值,除非您确切知道自己在做什么(例如,您在用户空间中,并且您已经确保没有信号处理程序可以异步使用堆栈)。堆栈指针*4+大常量不是普通程序所能做的事情


通常可以,但ESP是唯一在x86地址模式下不能作为索引的寄存器。请参阅 (索引是寻址模式的一部分,可以应用2位移位计数,也称为比例因子)

我认为我们最好的办法仍然是将ESP复制到EDI,然后使用LEA:

 mov  edi, esp
 lea  edi, [edi * 4 + 0x11223344]
或者你可以用LEA进行复制和加法,然后左移位,因为我们要加的值有两个零作为它的低位(也就是说,它是4的倍数)。所以我们可以将其右移2,而不会丢失任何位

SHIFTED_ADD_CONSTANT equ 0x11223344 >> 2

  lea    edi, [esp + SHIFTED_ADD_CONSTANT]
  shl    edi, 2
左移位前的加法将产生进位到前2位,但我们将要将这些位移出,这样就不管有什么了

这也是2个UOP,在AMD推土机系列CPU上效率更高(GP integer
mov
不消除mov,并且缩放索引会为LEA额外花费一个延迟周期)。Zen消除了mov,但我认为LEA延迟仍然相同,因此两个版本都是2周期延迟。即使是“复杂”LEA在Zen上的吞吐量也为2/时钟,而对于简单LEA(任何ALU端口),则为4/时钟

但在英特尔IvyBridge和更高版本的CPU上效率较低,
mov
可以零延迟运行(mov消除),而
[edi*4+disp32]
寻址模式仍然是一种快速的双组件LEA。因此,在消除mov的Intel CPU上,第一个版本是2个前端uop,一个执行单元1个未使用的域uop,只有1个延迟周期

另一个双指令选项是使用较慢的
imul
,而不是快速换档。(寻址模式使用移位:尽管它写为
*1/2/4/8
,但它编码在机器代码中的2位移位计数字段中)

imul
在现代x86 CPU上有3个周期的延迟,这相当不错,但在像奔腾3这样的旧CPU上速度较慢。mov+LEA的延迟仍然不如1或2个周期,并且
imul
在较少的端口上运行



(指令的数量通常不需要优化;UOP的数量通常更重要,延迟/后端吞吐量也更重要。代码大小以x86机器代码的字节为单位;不同的指令有不同的长度。)

为什么要执行
推送esp
?修改ESP并将旧值的副本存储到
[ESP]
的内存中,因为ESP是堆栈指针。。您的意思是
mov eax,esp
mul
设置隐式输入操作数吗?您确定您在ESP中有一个值,可以将其左移并添加一个大常量吗?通常避免使用堆栈指针来保存临时值。。。
  imul  edi, esp, 4       ; this is dumb, don't use mul/imul for powers of 2.
  add   edi, 0x11223344