使用条件移动(汇编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;
}
将生成优化的相关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
根据标准,条件表达式将只评估一个分支。但是这里两个分支都被评估,这违反了标准的语义。是否针对C标准进行了优化?还是许多编译器优化与语言标准不一致?
由于“按原样规则”,即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;
}
编译为
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
通过我的GCC Ubuntu 7.2.0-8ubuntu3.2
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句