在 C 函数中延迟创建自动变量
Lazy creation of auto variables inside C function
我对这样的 C 代码很好奇:
void test(int y){
if (y) {
int x = 1;
printf("Test: %d", x + y);
}
// other important code (don`t use x var)
}
x
变量的必要性取决于 y
。
例如:如果我们调用test(0)
,我们不需要为x
变量分配space,因为test(1)
调用x
变量必须存在于内存中。
现代编译器是否使用这种技术?
编译器更可能的优化是
void test(int y){
if (y) {
printf("Test: %d", 1 + y);
}
// other important code (don`t use x var)
}
然后在任何一种情况下我们都不需要 x
。
编译器所做的事情会根据优化级别而有所不同,但人们会期望在最高优化级别下,现代编译器会在寄存器中执行所有操作,其中 x
被常量折叠掉,因为它是一个常量 1
我们可以从 this godbolt session gcc 中看到:
test:
testl %edi, %edi
jne .L4
rep ret
.L4:
leal 1(%rdi), %esi
xorl %eax, %eax
movl $.LC0, %edi
jmp printf
在最低的优化级别,我们不期望任何聪明的东西,在这种情况下,gcc 为 x
和 y
分配,而不检查是否确实需要 x
:
subq , %rsp
正如 Sourav Ghosh 所说,"Nothing you can be certain of!",但是,让我给出编译器可能使用的一些可能性。
首先,从语法上讲,块中的变量仅在块内是已知的。在块之外,编译器(语言)已经完全忘记了它。所以你可以使用这个块构造来拥有一个临时变量。
编译器能做什么?
编译器可能会计算所有块的最大存储量(即函数入口(函数作用域)声明的自动变量加上最大块的大小,加上最大嵌套块的大小,.. . 等)并在堆栈上分配该数量。当一个块退出时,编译器知道它的变量不再存在并且可以为任何后续块重新使用 space (在堆栈上)。在 Fortran 中,这称为覆盖,它相当于 C 中的联合。
同样可能的是,编译器可能会在进入块时在堆栈上分配新的 space(例如 sub sp, 12
),并在离开块时释放它(例如 add sp, 12
)。
可能还有其他我不知道的策略。
我对这样的 C 代码很好奇:
void test(int y){
if (y) {
int x = 1;
printf("Test: %d", x + y);
}
// other important code (don`t use x var)
}
x
变量的必要性取决于 y
。
例如:如果我们调用test(0)
,我们不需要为x
变量分配space,因为test(1)
调用x
变量必须存在于内存中。
现代编译器是否使用这种技术?
编译器更可能的优化是
void test(int y){
if (y) {
printf("Test: %d", 1 + y);
}
// other important code (don`t use x var)
}
然后在任何一种情况下我们都不需要 x
。
编译器所做的事情会根据优化级别而有所不同,但人们会期望在最高优化级别下,现代编译器会在寄存器中执行所有操作,其中 x
被常量折叠掉,因为它是一个常量 1
我们可以从 this godbolt session gcc 中看到:
test:
testl %edi, %edi
jne .L4
rep ret
.L4:
leal 1(%rdi), %esi
xorl %eax, %eax
movl $.LC0, %edi
jmp printf
在最低的优化级别,我们不期望任何聪明的东西,在这种情况下,gcc 为 x
和 y
分配,而不检查是否确实需要 x
:
subq , %rsp
正如 Sourav Ghosh 所说,"Nothing you can be certain of!",但是,让我给出编译器可能使用的一些可能性。
首先,从语法上讲,块中的变量仅在块内是已知的。在块之外,编译器(语言)已经完全忘记了它。所以你可以使用这个块构造来拥有一个临时变量。
编译器能做什么?
编译器可能会计算所有块的最大存储量(即函数入口(函数作用域)声明的自动变量加上最大块的大小,加上最大嵌套块的大小,.. . 等)并在堆栈上分配该数量。当一个块退出时,编译器知道它的变量不再存在并且可以为任何后续块重新使用 space (在堆栈上)。在 Fortran 中,这称为覆盖,它相当于 C 中的联合。
同样可能的是,编译器可能会在进入块时在堆栈上分配新的 space(例如 sub sp, 12
),并在离开块时释放它(例如 add sp, 12
)。
可能还有其他我不知道的策略。