#include <stdint.h>
int main(void)
{
uint64_t a = 0x12345679abcdefULL;
uint64_t b = 0xfdcba987654321ULL;
uint64_t c = a + b;
return 0;
}
我刚刚使用
gcc -O0 -S
测试了代码。似乎在堆栈上分配了两个32位值,然后分别与addl
和adcl
相加。即使我使用的是64位计算机,-m32
和-m64
切换也会发生这种情况,并且确实确实存在一个专用于64位整数的指令集,包括movq
和addq
之类的操作。为什么我告诉
gcc
生成类似32位计算机的代码,即使我告诉它在-m64
上使用64位算术?>uname -a
CYGWIN_NT-6.2-WOW work 1.7.35(0.287/5/3) 2015-03-04 12:07 i686 Cygwin
>gcc --version
gcc (GCC) 4.9.2
现在,假设我在32位计算机上,并且我对64位整数进行操作。
该程序在上分配了两个32位变量堆栈。这是C / C ++标准的一部分,还是正在将它们分配给某些编译器所期望的堆(因为它们尝试以其他方式将64位整数适合32位堆栈)?
如果我将
movq
放入专为32位计算机设计的程序中,则会仿真所需的行为,还是会误解该指令?#1 楼
1:请参见此64位汇编参考上的“立即数”部分。
指令中的立即数保留为32位,并且其值符号扩展为64位
没有指令将64位立即数移动到内存(*)。而且由于可以通过移动两个32位块来很好地模拟它,因此没有理由引入新的64位指令。 (他们可以将立即数
mov
更改为始终使用64位。但是考虑到您将使用的大多数常量是<= 2 ^ 31,使用32位仅会节省大量零位高字节空间,并且花费了当您实际使用较大的常数时,此位可以节省内存)。(*)但是,有些指令将64位立即数移动到64位寄存器,因为您无法访问
我不知道为什么您的程序生成单独的addl / adcl指令;为什么?这是我从您的程序中得到的:
pushq %rbp
movq %rsp, %rbp
movl 41302511, -24(%rbp)
movl 93046, -20(%rbp)
movl $-2023406815, -16(%rbp)
movl 632745, -12(%rbp)
movq -16(%rbp), %rax
movq -24(%rbp), %rdx
leaq (%rdx,%rax), %rax
movq %rax, -8(%rbp)
movl q4312078q, %eax
leave
ret
您可以看到,
leaq (%rdx,%rax), %rax
可以添加64位数字。这是RHEL 6.6 64位系统上的gcc 4.4.7。请始终声明您的编译器和OS版本,因为输出可能完全取决于它们。2:
只要您使用的是x86 / amd64架构,您就可以依赖于放在堆栈中的局部变量,而不是放在堆栈中的全局变量。但是请注意,“堆栈”和“堆”的概念并未像看起来那样明确定义。不建议使用
brk/sbrk
分配内存的机制;现代实现使用mmap
。这可能意味着您在地址空间的不同部分中有几个小堆。在ARM和MIPS上,根本没有堆栈指针-只是一个约定,即一个特定的寄存器用作堆栈指针,但是压入/弹出指令也可以与其他寄存器一起使用(*)。从理论上讲,编译器可以在每个函数的开头随意分配一个mmap()
来分配本地内存,并在函数的结尾随意分配一个munmap()
。编译器唯一要做的就是在函数退出后不保留分配的内存(用于分配的合理定义)。(*)这有点过分简化,但是说明了这个概念。
当然,使用
mmap()
为局部变量腾出空间的想法是一个极端的例子,可能没人会使用。但是许多编译器将局部变量放入处理器寄存器中,并且从不为它们保留堆栈空间(如果您从未使用过指向它们的指针,以及在那些不像x86那样缺乏寄存器的架构上)。许多架构也将处理器寄存器用于函数参数。而且我见过微控制器C编译器,如果您使用某个关键字,则可以将函数本地的所有变量放入静态区域,这样编译器就知道该函数不是递归调用的。因此,尽管在大多数情况下,局部变量将被放置在堆栈上,但您不应该假定它是刻在石头上的。3.
该说明将被误解。处理器可以处于32位或64位模式,并且相同的指令(含义为:相同的指令字节序列)在每个指令中具有不同的含义。例如,
48 89 43 ec
在64位模式下为mov [rbx-20],rax
,但是dec eax; mov DWORD PTR [ebx-0x14],eax
在32位模式下。
评论
非常好的介绍答案,谢谢!还有两个问题:关于1-关于gcc如何具体决定使用哪个指令集(64/32位),我能读懂什么吗?关于2-使用mmap声音要比移动堆栈指针(是否标准化)昂贵得多。 ARM或MIPS编译器是否偏离您提到的约定?
– rr-
15年3月13日在15:04
gcc使用哪种指令集取决于-m32或-m64。使用该指令集中的确切指令取决于(至少)编译器版本号和优化。除了源代码本身,我认为没有关于此的全面文档。我对局部变量约定做了一些修改。
–贡特拉姆·布洛姆(Guntram Blohm)
15年3月13日在16:37