考虑以下代码片段:
for(i = 0; i<10; i++)
{
int n = a[i];//first loop statement
//other statements
}
显然,编译器不会将第一个语句提升出循环。但编译器是否只能在循环上方提升n的声明?换句话说,编译器也可以优化上面的代码:
int n;
for(i = 0; i < 10; i++)
{
n = a[i];//first loop statement
}
答案 0 :(得分:3)
实际上,大多数编译器甚至会在-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
。
即使在最低级别的优化中,编译器实现“在循环外提升变量声明”优化的方式是将所有块范围自动变量的声明提升到函数范围。绝对没有任何意义。在没有对目标语言的最小理解的情况下,对编译器优化的讨论也没有多大意义,其中变量声明不需要导致任何代码。