相关疑难解决方法(0)

为什么GCC会生成mov%eax,%eax以及它是什么意思?

GCC 4.4.3生成了以下x86_64程序集.令我困惑的部分是mov %eax,%eax.将寄存器移到自身?为什么?

   23b6c:       31 c9                   xor    %ecx,%ecx        ; the 0 value for shift
   23b6e:       80 7f 60 00             cmpb   $0x0,0x60(%rdi)  ; is it shifted?
   23b72:       74 03                   je     23b77
   23b74:       8b 4f 64                mov    0x64(%rdi),%ecx  ; is shifted so load shift value to ecx
   23b77:       48 8b 57 38             mov    0x38(%rdi),%rdx  ; map base
   23b7b:       48 03 57 58             add    0x58(%rdi),%rdx  ; plus offset to value
   23b7f:       8b 02                   mov    (%rdx),%eax      ; load map_used value to eax …
Run Code Online (Sandbox Code Playgroud)

assembly gcc x86-64

27
推荐指数
1
解决办法
9194
查看次数

cltq在装配中做了什么?

0x0000000000400553 <main+59>:   mov    -0x4(%rbp),%eax
0x0000000000400556 <main+62>:   cltq   
0x0000000000400558 <main+64>:   shl    $0x3,%rax
0x000000000040055c <main+68>:   mov    %rax,%rdx
Run Code Online (Sandbox Code Playgroud)

事实上,我的程序很简单:

5   int main(int argc, char *argv[]) { 
6     int i = 0;
7     while(environ[i]) {
8       printf("%s\n", environ[i++]);
9     }
10    return 0;
Run Code Online (Sandbox Code Playgroud)

但是程序集输出很长:

Dump of assembler code for function main:
0x0000000000400518 <main+0>:    push   %rbp
0x0000000000400519 <main+1>:    mov    %rsp,%rbp
0x000000000040051c <main+4>:    sub    $0x20,%rsp
0x0000000000400520 <main+8>:    mov    %edi,-0x14(%rbp)
0x0000000000400523 <main+11>:   mov    %rsi,-0x20(%rbp)
0x0000000000400527 <main+15>:   movl   $0x0,-0x4(%rbp)
0x000000000040052e <main+22>:   jmp    0x400553 <main+59>
0x0000000000400530 <main+24>:   mov    -0x4(%rbp),%eax …
Run Code Online (Sandbox Code Playgroud)

x86 assembly gnu-assembler att

26
推荐指数
4
解决办法
4万
查看次数

x86的MOV真的可以"免费"吗?为什么我不能重现这个呢?

我一直看到人们声称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)

c x86 assembly cpu-registers micro-optimization

23
推荐指数
2
解决办法
2113
查看次数

R代表RAX,RBX,RCX,RDX,RSI,RDI,RBP,RSP?

x86汇编语言不得不改变,因为x86处理器架构已经从8位变为16位变为32位,现在变为64位.

我知道在32位汇编程序寄存器名称(EAX,EBX等)中,每个名称的E前缀代表扩展意味着32位形式的寄存器而不是16位形式(AX,BX等).

这些寄存器名称的R前缀在64位中代表什么?

x86 assembly x86-64 cpu-architecture cpu-registers

20
推荐指数
2
解决办法
2万
查看次数

x86-64中movq和movabsq之间的区别

我是这里的新手,刚刚开始学习汇编语言.所以,如果我错了,请纠正我,或者如果这篇文章没有任何意义我会删除.

我在讨论x86-64英特尔架构中的数据移动指令.我已经读过,常规movq指令只能有直接的源操作数,可以表示为32位二进制补码数,而movabsq指令可以有任意64位立即数作为其源操作数,并且只能有一个寄存器作为目标.

你能详细说明一下吗?这是否意味着我只能使用movabsq指令移动64位立即值?只有立即价值到登记册?我不知道如何将64位立即值移动到内存中.或者也许我错了一些重要的事情.

x86 assembly x86-64 instruction-set att

19
推荐指数
1
解决办法
8093
查看次数

将32位偏移量添加到x86-64 ABI的指针时是否需要符号或零扩展?

简介:我正在查看汇编代码来指导我的优化,并在将int32添加到指针时看到许多符号或零扩展.

void Test(int *out, int offset)
{
    out[offset] = 1;
}
-------------------------------------
movslq  %esi, %rsi
movl    $1, (%rdi,%rsi,4)
ret
Run Code Online (Sandbox Code Playgroud)

起初,我认为我的编译器在添加32位到64位整数时遇到了挑战,但我已经用Intel ICC 11,ICC 14和GCC 5.3证实了这种行为.

这个帖子证实了我的发现,但不清楚是否需要符号或零扩展.仅当尚未设置高32位时,才需要此符号/零扩展.但x86-64 ABI难道不够聪明吗?

我有点不愿意将所有指针偏移更改为ssize_t,因为寄存器溢出会增加代码的缓存占用空间.

assembly x86-64 abi compiler-optimization sign-extension

16
推荐指数
1
解决办法
2591
查看次数

将bool从参数复制到全局 - 比较编译器输出

完全知道这些完全人为的基准测试并不重要,但我对"大4"编译器选择编写一个简单的片段的几种方式感到有点惊讶.

struct In {
    bool in1;
    bool in2;
};

void foo(In &in) {
    extern bool out1;
    extern bool out2;
    out1 = (in.in1 == true);
    out2 = in.in2;
}
Run Code Online (Sandbox Code Playgroud)

注意:所有编译器都设置为x64模式,具有最高的"通用"(=没有指定特定的处理器体系结构)"优化速度"设置; 你可以通过自己看到的结果/和他们一起玩了,在https://gcc.godbolt.org/z/K_i8h9)


带有-O3的Clang 6似乎产生了最直接的输出:

foo(In&):                             # @foo(In&)
        mov     al, byte ptr [rdi]
        mov     byte ptr [rip + out1], al
        mov     al, byte ptr [rdi + 1]
        mov     byte ptr [rip + out2], al
        ret
Run Code Online (Sandbox Code Playgroud)

在符合标准的C++程序中,== true比较是多余的,因此两个分配都成为从一个内存位置到另一个内存位置的直接副本,al因为内存没有内存mov.

但是,由于这里没有寄存器压力,我原本期望它使用两个不同的寄存器(完全避免两个赋值之间的错误依赖链),可能先启动所有读操作,然后执行所有写操作,以帮助指令级并行; 由于寄存器重命名和积极无序的CPU,这种优化是否已经完全淘汰了最近的CPU?(稍后会详细介绍)


带有-O3的GCC 8.2 …

c++ x86 assembly micro-optimization compiler-optimization

15
推荐指数
2
解决办法
439
查看次数

JMP指令 - 十六进制代码

对JMP机器指令的十六进制代码转换有疑问.我有要跳转的绝对地址,说"JMP 0x400835".首先,这是允许的吗?如果是,相应的十六进制代码是什么?如果没有,我可以先将地址存储在某个寄存器中,比如EAX,然后输入"JMP EAX"吗?我正在研究x86(64b)架构.

我试图从gdb中的diassem输出打印出十六进制代码,但没有一致性,即我没有在十六进制代码中看到目标地址.

我是十六进制代码和机器指令的新手,所以请原谅我的无知.

hex x86-64

13
推荐指数
2
解决办法
3万
查看次数

为什么GCC不使用部分寄存器?

write(1,"hi",3)在linux上反汇编,gcc -s -nostdlib -nostartfiles -O3结果如下:

ba03000000     mov edx, 3 ; thanks for the correction jester!
bf01000000     mov edi, 1
31c0           xor eax, eax
e9d8ffffff     jmp loc.imp.write
Run Code Online (Sandbox Code Playgroud)

我不是到编译器的开发,但由于移动到这些寄存器的每一个值是恒定的和已知的编译时间,我很好奇,为什么不GCC使用dl,dil和al来代替.也许有人会说,此功能不会让任何性能上的差异,但有一个在之间的可执行文件的大小有很大的区别mov $1, %rax => b801000000,并mov $1, %al => b001当我们谈论数千寄存器的程序访问.如果软件的优雅部分不仅体积小,它确实会对性能产生影响.

有人可以解释为什么"海湾合作委员会决定"它无所谓?

x86 assembly gcc x86-64

13
推荐指数
2
解决办法
1655
查看次数

如果只需要结果的低部分,那么可以使用哪个2的补码整数运算而不将输入中的高位置零?

在汇编编程中,想要从寄存器的低位计算某些东西是相当普遍的,这些位不能保证将其他位置零.在像C这样的高级语言中,你只需将输入转换为小尺寸,让编译器决定是否需要分别将每个输入的高位归零,或者是否可以在输出之后切断结果的高位.事实.

这是为x86-64的(又名AMD64),出于各种原因尤其常见1,其中的一些是存在于其它的ISA.

我将使用64位x86作为示例,但目的是询问/讨论2的补码和无符号二进制算法,因为所有现代CPU都使用它.(注意,C和C++不保证两个补码4,并且有符号溢出是未定义的行为.)

作为示例,考虑一个可以编译为LEA指令2的简单函数.(在X86-64 SysV的(Linux)的ABI 3,前两个函数参数是rdi和rsi,与在返回rax. int是一个32位的类型.)

; int intfunc(int a, int b) { return a + b*4 + 3; }
intfunc:
    lea  eax,  [edi + esi*4 + 3]  ; the obvious choice, but gcc can do better
    ret
Run Code Online (Sandbox Code Playgroud)

gcc知道即使是负有符号整数,加法也只是从右到左,所以输入的高位不会影响进入的内容eax.因此,它保存了一个指令字节并使用 lea eax, [rdi + rsi*4 + 3]

哪些其他操作具有结果低位的这种属性而不依赖于输入的高位?

为什么它有效?



脚注

1为什么x86-64频繁出现这种情况:x86-64有可变长度指令,其中额外的前缀字节改变了操作数大小(从32到64或16),因此在指令中通常可以保存一个字节.以相同的速度执行.当写入低8b或16b的寄存器(或稍后读取完整寄存器(Intel pre-IvB)时的失速)时,它也具有错误依赖性(AMD/P4/Silvermont):由于历史原因, …

binary x86 assembly integer twos-complement

12
推荐指数
1
解决办法
928
查看次数