assembly - 编译器通常使用寄存器来实现 "intended"的目的吗?

标签 assembly compilation compiler-construction x86 x86-64

我一直在学习汇编,并且我了解到四个主要的 x86 通用寄存器(eax、ebx、ecx 和 edx)每个都有一个预期或建议的用途。例如,eax是累加器寄存器,ecx用作循环的计数器,等等。大多数编译器是否尝试将寄存器用于建议的目的,或者它们是否忽略寄存器“应该”的用途,而只是将值分配给下一个可用寄存器?

此外,在查看 x64 寄存器时,我注意到添加了额外的 8 个通用寄存器,如果忽略 rbp、rsp、rsi 和 rdi(因为它们具有非通用用途),如果您确实包含它们,则为十六个。在普通用户程序(即浏览器、文字处理器等,而不是需要大量寄存器的加密程序)中,在任何给定时间通常使用多少个寄存器?对于像 Firefox 这样的程序来说,同时使用所有 12/16 个普通寄存器是否很常见,或者它们只使用一个子集,因为它们没有足够的变量来填充所有寄存器?我将通过反汇编二进制文件来亲自研究这个问题,看看一般情况是什么,但我希望得到比我更有知识的人的回答。

此外,如果半 gp 寄存器(rsi、rdi、rsp 和 rbp)当前未用于非通用应用程序,编译器通常是否将其用于通用用途?我很好奇,因为我看到这些寄存器被列为“通用”,但即使我也能想到这些寄存器不能用于通用存储的实例(例如,您不想存储变量到 rbp 和 rsp,然后将值压入堆栈!)。那么编译器会在可能的情况下尝试使用这些寄存器吗? x86 和 x64 编译之间有区别吗?因为 x64 处理器有更多可用寄存器,因此不需要将变量填充到任何可用寄存器中?

最佳答案

所有 GP 寄存器都是通用的。
仅当执行特定的(通常是遗留的)指令时,它们才具有特殊含义。

例如四元组rsirdirbprsp,只有后者有特殊用途,这是由于 callretpush 等指令造成的。
如果您不使用它们,即使是隐式使用(诚然,这是一种不太可能的情况),您也可以将其用作累加器。

这个原则是通用的,编译器会利用它。

考虑这个人为的例子[ 1 ]:

void maxArray(int* x, int* y, int*z, short* w) {
    for (int i = 0; i < 65536; i++)
    {
        int a = y[i]*z[i];
        int b = z[i]*z[i];
        int c = y[i]*x[i]-w[i];
        int d = w[i]+x[i]-y[i];
        int e = y[i+1]*w[i+2];
        int f = w[i]*w[i];

        x[i] = a*a-b+d; 
        y[i] = b-c*d/f+e;
        z[i] = (e+f)*2-4*a*d;
        w[i] = a*b-c*d+e*f;
    }
}

它由 GCC 编译到此列表中

maxArray(int*, int*, int*, short*):
        push    r13
        push    r12
        xor     r8d, r8d
        push    rbp
        push    rbx
        mov     r12, rdx
.L2:
        mov     edx, DWORD PTR [rsi+r8*2]    
        mov     ebp, DWORD PTR [r12+r8*2]
        movsx   r11d, WORD PTR [rcx+r8]
        mov     eax, DWORD PTR [rdi+r8*2]
        movsx   ebx, WORD PTR [rcx+4+r8]
        mov     r9d, edx
        mov     r13d, edx
        imul    r9d, ebp
        imul    r13d, eax
        lea     r10d, [rax+r11]
        imul    ebx, DWORD PTR [rsi+4+r8*2]
        mov     eax, r9d
        sub     r10d, edx
        imul    ebp, ebp
        sub     r13d, r11d
        imul    eax, r9d
        imul    r11d, r11d
        sub     eax, ebp
        add     eax, r10d
        mov     DWORD PTR [rdi+r8*2], eax
        mov     eax, r13d
        imul    eax, r10d
        cdq
        idiv    r11d
        mov     edx, ebp
        sub     edx, eax
        mov     eax, edx
        lea     edx, [0+r9*4]
        add     eax, ebx
        mov     DWORD PTR [rsi+r8*2], eax
        lea     eax, [rbx+r11]
        imul    r9d, ebp
        imul    r11d, ebx
        add     eax, eax
        imul    edx, r10d
        add     r9d, r11d
        imul    r10d, r13d
        sub     eax, edx
        sub     r9d, r10d
        mov     DWORD PTR [r12+r8*2], eax
        mov     WORD PTR [rcx+r8], r9w
        add     r8, 2
        cmp     r8, 131072
        jne     .L2
        pop     rbx
        pop     rbp
        pop     r12
        pop     r13
        ret

可以看到大部分GP寄存器都被使用了(我没有统计过),包括rbprsirdi .
寄存器的用途均不限于其规范形式。

注意 在此示例中,rsirdi 用于加载和读取(均针对每个寄存器)数组,这是巧合。
这些寄存器用于传递前两个整数/指针参数。

int sum(int a, int b, int c, int d)
{
    return a+b+c+d;
}

sum(int, int, int, int):
        lea     eax, [rdi+rsi]
        add     eax, edx
        add     eax, ecx
        ret

关于assembly - 编译器通常使用寄存器来实现 "intended"的目的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43260983/

相关文章:

汇编除以零溢出

python - 如何将包含多个文件的 Python 程序编译为 mac?

c - 为什么这段代码可以编译但实际上不起作用?

javascript - Elixir如何组合$(document).ready(function(){}

parsing - 每个 LL(1) 文法也是 LALR(1) 文法吗?

objective-c - 程序集 Objective-C 类分配错误

assembly - 是什么导致此引导加载程序在硬件上失败但在 DOSBOX 中没有?它显示所有寄存器

assembly - 缓冲输入如何工作

从另一个 C 文件调用函数

python - 如何用 Python 编写抽象语法树的访问者模式?