1:
请参阅此 64 位程序集参考中的“立即数”部分。
指令中的立即数保持 32 位,并且它们的值被符号扩展到 64 位
只是没有将 64 位立即数移动到内存(*)的指令。并且因为可以通过移动两个 32 位块来很好地模拟它,所以没有理由引入新的 64 位指令。(他们本可以将立即数更改mov
为始终使用 64 位。但考虑到您将使用的大多数常量 <= 2^31,使用 32 位只会为高位零字节节省大量空间,并且成本有点高当您实际使用更大的常量时,这样可以节省内存)。
(*) 但是,有一些指令可以将 64 位立即数移动到 64 位寄存器,因为您无法访问寄存器中的高 32 位,而不是内存。
我不知道为什么你的程序产生了单独的 addl/adcl 指令;这是我从你的程序中得到的:
pushq %rbp
movq %rsp, %rbp
movl $2041302511, -24(%rbp)
movl $1193046, -20(%rbp)
movl $-2023406815, -16(%rbp)
movl $16632745, -12(%rbp)
movq -16(%rbp), %rax
movq -24(%rbp), %rdx
leaq (%rdx,%rax), %rax
movq %rax, -8(%rbp)
movl $0, %eax
leave
ret
如您所见,leaq (%rdx,%rax), %rax
添加 64 位数字没问题。这是 RHEL 6.6 64 位系统上的 gcc 4.4.7。请始终说明您的编译器和操作系统版本,因为输出可能非常依赖于这些。
2:
只要您处理的是 x86/amd64 架构,您就可能依赖于放置在堆栈中的局部变量和不在堆栈中的全局变量。但请注意,“堆栈”和“堆”的概念并不像看起来那么明确。brk/sbrk
不推荐使用分配内存的机制;现代实现使用mmap
. 这可能意味着您在地址空间的不同部分有几个小堆。在 ARM 和 MIPS 上,根本没有堆栈指针 - 只有一个特定寄存器用作堆栈指针的约定,但推送/弹出指令也可以与其他寄存器一起使用(*)。理论上,编译器可以mmap()
在每个函数开始时自由地做一个来分配本地内存,并且munmap()
它在函数的末尾。编译器必须做的唯一一件事就是在函数退出后不保留已分配的内存(对于已分配的合理定义)。
(*) 这有点过于简单化,但演示了这个概念。
当然,mmap()
用于为局部变量腾出空间的想法是一个极端的例子,可能没有人使用。但是许多编译器将局部变量放入处理器寄存器中,并且从不为它们保留堆栈空间(如果您从不使用指向它们的指针,并且在不像 x86 那样缺乏寄存器的体系结构上)。许多架构也将处理器寄存器用于函数参数。而且我已经看到微控制器 C 编译器允许您将函数的所有局部变量放入静态区域,如果您使用某个关键字,则编译器知道该函数不是递归调用的。因此,虽然大多数情况下,局部变量将被放置在堆栈中,但您不应认为这是一成不变的。
3.
指令会被误解。处理器可以是32位或64位模式,相同的指令(含义:指令字节序列相同)在每条指令中都有不同的含义。例如,48 89 43 ec
是mov [rbx-20],rax
在64位模式,但dec eax; mov DWORD PTR [ebx-0x14],eax
在32位模式。