man*_*old 25 c assembly ternary-operator compiler-optimization language-lawyer
使用条件移动(汇编cmov
)来优化?:
C中的条件表达式是一种常见的优化.但是,C标准说:
第一个操作数被评估; 在其评估与第二或第三操作数的评估之间存在一个序列点(以评估者为准).仅当第一个操作数不等于0时才评估第二个操作数; 仅当第一个操作数比较等于0时才评估第三个操作数; 结果是第二个或第三个操作数的值(无论哪个被评估),转换为下面描述的类型.110)
例如,以下C代码
#include <stdio.h>
int main() {
int a, b;
scanf("%d %d", &a, &b);
int c= a > b ? a + 1 : 2 + b;
printf("%d", c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
将生成优化的相关asm代码,如下所示:
call __isoc99_scanf
movl (%rsp), %esi
movl 4(%rsp), %ecx
movl $1, %edi
leal 2(%rcx), %eax
leal 1(%rsi), %edx
cmpl %ecx, %esi
movl $.LC1, %esi
cmovle %eax, %edx
xorl %eax, %eax
call __printf_chk
Run Code Online (Sandbox Code Playgroud)
根据标准,条件表达式将仅评估一个分支.但是这里对两个分支进行了评估,这违反了标准的语义.这是针对C标准的优化吗?或者许多编译器优化是否与语言标准不一致?
Ant*_*ala 42
由于"as-if规则",即C11 5.1.2.3p6,优化是合法的.
只需要一个符合要求的实现来生成一个程序,该程序在运行时产生与使用抽象语义产生的程序执行相同的可观察行为.标准的其余部分只描述了这些抽象语义.
什么是编译程序不会在内部完全不打紧,唯一重要的事情是,在程序结束时不会有任何其他观察到的行为,除了阅读a
和b
与印刷的值a + 1
或者b + 2
取决于哪一个a
或b
更大,除非发生导致行为未定义的事情.(输入错误导致a,b未初始化,因此访问未定义;范围错误和签名溢出也可能发生.)如果发生未定义的行为,则所有投注都将关闭.
由于必须严格根据抽象语义来评估对volatile变量的访问,因此可以通过以下方式去除条件移动volatile
:
#include <stdio.h>
int main() {
volatile int a, b;
scanf("%d %d", &a, &b);
int c = a > b ? a + 1 : 2 + b;
printf("%d", c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译成
call __isoc99_scanf@PLT
movl (%rsp), %edx
movl 4(%rsp), %eax
cmpl %eax, %edx
jg .L7
movl 4(%rsp), %edx
addl $2, %edx
.L3:
leaq .LC1(%rip), %rsi
xorl %eax, %eax
movl $1, %edi
call __printf_chk@PLT
[...]
.L7:
.cfi_restore_state
movl (%rsp), %edx
addl $1, %edx
jmp .L3
Run Code Online (Sandbox Code Playgroud)
由我的GCC Ubuntu 7.2.0-8ubuntu3.2
Jen*_*ens 26
C标准描述了执行C代码的抽象机器.只要不违反抽象,编译器就可以自由地执行任何优化,即符合条件的程序无法区分.
归档时间: |
|
查看次数: |
2408 次 |
最近记录: |