【发布时间】:2016-07-17 20:25:00
【问题描述】:
我编写了一个 C 程序,它只读取/写入一个大数组。我用命令gcc -O0 program.c -o program 编译了程序出于好奇,我用objdump -S 命令反汇编了C 程序。
read_array和write_array函数的代码和汇编附在本题末尾。
我试图解释 gcc 是如何编译函数的。我使用// 添加我的 cmets 和问题
取一段开头的write_array()函数的汇编代码
4008c1: 48 89 7d e8 mov %rdi,-0x18(%rbp) // this is the first parameter of the fuction
4008c5: 48 89 75 e0 mov %rsi,-0x20(%rbp) // this is the second parameter of the fuction
4008c9: c6 45 ff 01 movb $0x1,-0x1(%rbp) // comparing with the source code, I think this is the `char tmp` variable
4008cd: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp) // this should be the `int i` variable.
我不明白的是:
1) char tmp 显然是在write_array 函数中定义之后 int i。为什么 gcc 对这两个局部变量的内存位置进行重新排序?
2) 从偏移量来看,int i 在-0x8(%rbp),char tmp 在-0x1(%rbp),这说明变量int i 占用7 个字节?这很奇怪,因为int i 在 x86-64 机器上应该是 4 个字节。不是吗?我的猜测是 gcc 试图做一些对齐?
3) 我发现 gcc 优化选项非常有趣。是否有一些好的文档/书籍可以解释 gcc 的工作原理? (第三个问题可能跑题了,如果你这么认为,请忽略。我只是想看看是否有一些捷径可以了解 gcc 用于编译的底层机制。:-))
下面是一段功能代码:
#define CACHE_LINE_SIZE 64
static inline void
read_array(char* array, long size)
{
int i;
char tmp;
for ( i = 0; i < size; i+= CACHE_LINE_SIZE )
{
tmp = array[i];
}
return;
}
static inline void
write_array(char* array, long size)
{
int i;
char tmp = 1;
for ( i = 0; i < size; i+= CACHE_LINE_SIZE )
{
array[i] = tmp;
}
return;
}
下面是 write_array 的反汇编代码,来自 gcc -O0:
00000000004008bd <write_array>:
4008bd: 55 push %rbp
4008be: 48 89 e5 mov %rsp,%rbp
4008c1: 48 89 7d e8 mov %rdi,-0x18(%rbp)
4008c5: 48 89 75 e0 mov %rsi,-0x20(%rbp)
4008c9: c6 45 ff 01 movb $0x1,-0x1(%rbp)
4008cd: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
4008d4: eb 13 jmp 4008e9 <write_array+0x2c>
4008d6: 8b 45 f8 mov -0x8(%rbp),%eax
4008d9: 48 98 cltq
4008db: 48 03 45 e8 add -0x18(%rbp),%rax
4008df: 0f b6 55 ff movzbl -0x1(%rbp),%edx
4008e3: 88 10 mov %dl,(%rax)
4008e5: 83 45 f8 40 addl $0x40,-0x8(%rbp)
4008e9: 8b 45 f8 mov -0x8(%rbp),%eax
4008ec: 48 98 cltq
4008ee: 48 3b 45 e0 cmp -0x20(%rbp),%rax
4008f2: 7c e2 jl 4008d6 <write_array+0x19>
4008f4: 5d pop %rbp
4008f5: c3 retq
【问题讨论】:
-
int必须是 4 字节对齐的,并且不能位于-0x4,因为这会导致它与字符重叠。 -
重点:我不相信 gcc 会重新排序参数,除非调用者和被调用者都在同一个编译单元中(因为它们似乎在这里)。
-
在
-O3以下级别做出的决定并不重要 -
@M.M 如果
int位于-0x4,那么char将在其他地方,呵呵。 -
-O0只有在调试时才真正有用(C 代码和底层程序集之间通常有更清晰的关联)。在过去,调试优化代码更加困难,但对于现代 GCC 和 GDB,这确实不是问题。在一些编译器的早期,优化器存在错误,因此退回到不进行优化是避免问题的一种技巧。如果您想了解标准调用约定,-O0也很有价值。发布软件时,我想不出使用-O0的理由(没有优化)。-O2是我使用的最低要求。
标签: c linux gcc assembly disassembly