实际上,大多数编译器即使在-O0 也会这样做:
~ $ cat t.c
volatile int v;
int a[10];
void f(void)
{
int n;
int i;
for(i = 0; i < 10; i++) {
n = a[i];
v = n;
}
}
~ $ clang -S -O0 t.c
~ $ cat t.s
…
_f: ## @f
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
movl $0, -8(%rbp)
LBB0_1: ## =>This Inner Loop Header: Depth=1
cmpl $10, -8(%rbp)
jge LBB0_4
## BB#2: ## in Loop: Header=BB0_1 Depth=1
movq _v@GOTPCREL(%rip), %rax
movq _a@GOTPCREL(%rip), %rcx
movslq -8(%rbp), %rdx
movl (%rcx,%rdx,4), %esi
movl %esi, -4(%rbp)
movl -4(%rbp), %esi
movl %esi, (%rax)
## BB#3: ## in Loop: Header=BB0_1 Depth=1
movl -8(%rbp), %eax
addl $1, %eax
movl %eax, -8(%rbp)
jmp LBB0_1
LBB0_4:
popq %rbp
ret
…
~ $
请注意,上面的循环主体内没有任何指令可以保留n。相反,相同的堆栈槽-4(%rbp) 被无缝重用。如果我用最轻微的优化进行编译,甚至不会有n 的堆栈槽:一个寄存器足以在它所拥有的短时间内保存它的值:
~ $ clang -S -O1 t.c
~ $ cat t.s
…
_f: ## @f
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
xorl %eax, %eax
movq _a@GOTPCREL(%rip), %rcx
movq _v@GOTPCREL(%rip), %rdx
.align 4, 0x90
LBB0_1: ## =>This Inner Loop Header: Depth=1
movl (%rcx,%rax,4), %esi
movl %esi, (%rdx)
incq %rax
cmpq $10, %rax
jne LBB0_1
## BB#2:
popq %rbp
ret
在这个新编译的版本中,%esi 是n。
编译器实现“在循环外提升变量声明”优化的方式,即使是在最低级别的优化,也是通过将所有块范围自动变量的声明提升到函数范围。绝对没有什么。此外,如果对目标语言有最少的了解,那么关于编译器优化的讨论也没有多大意义,在目标语言中,变量声明不需要产生任何代码。