Lea*_*ess 3 x86 assembly opcodes
你为什么要用:
MOV EAX, 22
SHL EAX, 2
Run Code Online (Sandbox Code Playgroud)
...乘以4而不是仅仅使用MUL指令?
我知道这也可以用SHR而不是DIV.
这样做有什么好处?
你也可以用奇数做这个或者它只能是偶数吗?
有许多代码习惯用法比"MUL常量"快.
现代x86 CPU在几个时钟内执行MUL,最小值.因此,在1-2个时钟内计算产品的任何代码序列都将胜过MUL.您可以使用快速指令(ADD,SHL,LEA,NEG)以及处理器可以在单个时钟中并行执行其中一些指令以替换MUL的事实.可以说这意味着如果您避免某些数据依赖性,您可以在2个时钟中以多种组合执行其中4条指令.
LEA指令特别有趣,因为它可以乘以一些小常量(1,2,3,4,5,8,9),并将产品移动到另一个寄存器,这是打破数据依赖性的一种简单方法.这允许您在不破坏原始操作数的情况下计算子产品.
一些例子:
将EAX乘以5,将产品移至ESI:
LEA ESI, [EAX+4*EAX] ; this takes 1 clock
Run Code Online (Sandbox Code Playgroud)
将EAX乘以18:
LEA EAX, [EAX + 8*EAX]
SHL EAX, 1
Run Code Online (Sandbox Code Playgroud)
将EAX乘以7,将结果移至EBX:
LEA EBX, [8*EAX]
SUB EBX, EAX
Run Code Online (Sandbox Code Playgroud)
将EAX乘以28:
LEA EBX, [8*EAX]
LEA ECX, [EAX+4*EAX] ; this and previous should be executed in parallel
LEA EAX, [EBX+4*ECX]
Run Code Online (Sandbox Code Playgroud)
乘以1020:
LEA ECX, [4*EAX]
SHL EAX, 10 ; this and previous instruction should be executed in parallel
SUB EAX, ECX
Run Code Online (Sandbox Code Playgroud)
乘以35
LEA ECX, [EAX+8*EAX]
NEG EAX ; = -EAX
LEA EAX, [EAX+ECX*4]
Run Code Online (Sandbox Code Playgroud)
因此,当你想要达到乘以适度大小常数的效果时,你必须考虑如何将它"分解"到LEA指令可以产生的各种产品中,以及如何移动,添加或减去 a部分结果得到最终答案.
值得注意的是,通过这种方式可以产生多少乘法常数.您可能认为这仅适用于非常小的常量,但正如您从上面的1020示例中可以看到的那样,您也可以获得一些令人惊讶的中等大小的常量.当索引到结构数组时,事实证明这非常方便,因为你必须将索引乘以结构的大小.通常在索引这样的数组时,您需要计算元素地址并获取值; 在这种情况下,您可以将最终的LEA指令合并到MOV指令中,而不能使用真正的MUL.通过这种类型的习惯用法,可以购买额外的时钟周期来完成MUL.
[我已经构建了一个编译器,通过对指令组合进行小的穷举搜索,使用这些指令计算"最佳乘以常数"; 然后它缓存该答案以供以后重用].