我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.
对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.
例如,这是我用Visual C++编译的代码:
#include <limits.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
unsigned int k, l, j;
clock_t tstart = clock();
for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
{
++k;
k = j; // <-- comment out this line to remove the MOV instruction
l += j;
}
fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
fflush(stderr);
return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)
这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):
LOOP:
add edi,esi
mov …Run Code Online (Sandbox Code Playgroud) (如果是这样的话,我会自己做的.)
我的问题:
为方便起见,我倾向于避免间接/索引寻址模式.
作为替代,我经常使用立即,绝对或寄存器寻址.
代码:
; %esi has the array address. Say we iterate a doubleword (4bytes) array.
; %ecx is the array elements count
(0x98767) myloop:
... ;do whatever with %esi
add $4, %esi
dec %ecx
jnz 0x98767;
Run Code Online (Sandbox Code Playgroud)
在这里,我们有一个序列化的组合(dec和jnz),它可以防止正常的乱序执行(依赖).
有没有办法避免/破坏dep?(我不是装配专家).
我试图了解地址计算指令的工作原理,尤其是leaq命令.然后当我看到leaq用于进行算术运算的例子时,我感到困惑.例如,以下C代码,
long m12(long x) {
return x*12;
}
Run Code Online (Sandbox Code Playgroud)
在组装中
leaq (%rdi, %rdi, 2), %rax
salq $2, $rax
Run Code Online (Sandbox Code Playgroud)
如果我的理解是正确的,那么leaq应该移动任何(%rdi, %rdi, 2)应该2*%rdi+%rdi评估的地址%rax.我感到困惑的是,因为值x存储%rdi在内,这只是内存地址,为什么%rdi乘以3然后左移这个内存地址 2等于x乘以12?是不是当我们%rdi用3时,我们跳到另一个没有值x的内存地址?
我学到了一个地址,两个地址和三个地址指令,但是现在我想知道x86使用什么样的地址指令?
我了解的是,指令融合有两种类型:
微操作是指可以在1个时钟周期内执行的操作。如果几个微操作融合在一起,我们将获得一个“指令”。
如果融合了多条指令,我们将获得宏操作。
如果几个宏操作融合在一起,我们将获得宏操作融合。
我对么?