说我有这样的功能:
inline int shift( int what, int bitCount )
{
return what >> bitCount;
}
每次bitCount
为非负且在int
中的位数内时,将从不同的网站调用它。我特别关注bitCount
等于零的呼叫 - 它会正常工作吗?
在编译其调用站点时编译器是否有可能看到函数的整个代码会将bitCount
等于零的调用减少为无操作?
答案 0 :(得分:22)
According to K&R“如果右操作数为负数,或者大于或等于左表达式类型中的位数,则结果未定义。” (A.7.8)因此>> 0是身份右移,完全合法。
答案 1 :(得分:17)
某些至少有一个C ++编译器会识别这种情况(在编译时知道0时)并使其成为无操作:
<强>来源
inline int shift( int what, int bitcount)
{
return what >> bitcount ;
}
int f() {
return shift(42,0);
}
编译器开关
icpc -S -O3 -mssse3 -fp-model fast=2 bitsh.C
英特尔C ++ 11.0程序集
# -- Begin _Z1fv
# mark_begin;
.align 16,0x90
.globl _Z1fv
_Z1fv:
..B1.1: # Preds ..B1.0
movl $42, %eax #7.10
ret #7.10
.align 16,0x90
# LOE
# mark_end;
.type _Z1fv,@function
.size _Z1fv,.-_Z1fv
.data
# -- End _Z1fv
.data
.section .note.GNU-stack, ""
# End
正如您在..B1.1中看到的那样,英特尔将“返回班次(42,0)”编译为“返回42”。
英特尔11还剔除了这两种变化的转变:
int g() {
int a = 5;
int b = 5;
return shift(42,a-b);
}
int h(int k) {
return shift(42,k*0);
}
对于在编译时移位值不可知的情况......
int egad(int m, int n) {
return shift(42,m-n);
}
......这种转变是无法避免的......
# -- Begin _Z4egadii
# mark_begin;
.align 16,0x90
.globl _Z4egadii
_Z4egadii:
# parameter 1: 4 + %esp
# parameter 2: 8 + %esp
..B1.1: # Preds ..B1.0
movl 4(%esp), %ecx #20.5
subl 8(%esp), %ecx #21.21
movl $42, %eax #21.10
shrl %cl, %eax #21.10
ret #21.10
.align 16,0x90
# LOE
# mark_end;
...但至少它是内联的,因此没有呼叫开销。
奖金组装:挥发性很贵。来源......
int g() {
int a = 5;
volatile int b = 5;
return shift(42,a-b);
}
...而不是无操作,编译为......
..B3.1: # Preds ..B3.0
pushl %esi #10.9
movl $5, (%esp) #12.18
movl (%esp), %ecx #13.21
negl %ecx #13.21
addl $5, %ecx #13.21
movl $42, %eax #13.10
shrl %cl, %eax #13.10
popl %ecx #13.10
ret #13.10
.align 16,0x90
# LOE
# mark_end;
...所以,如果你在一台机器上工作,当你弹出它们时,你在堆栈上推送的值可能会不一样,那么这个错过的优化可能是你遇到的麻烦最少的。
答案 2 :(得分:4)
它可以在任何广泛使用的架构上正常工作(我可以担保x86,PPC,ARM)。除非函数内联,否则编译器将无法将其减少为noop。
答案 3 :(得分:3)
如果编译器在编译时知道bitCount值为零,则编译器只能执行此优化。这意味着传递的参数必须是常量:
const int N = 0;
int x = shift( 123, N );
C ++当然允许执行这样的优化,但我不知道有任何编译器这样做。编译器可以采用的替代方法:
int x = n == 0 ? 123 : shift( 123, n );
在大多数情况下会是一种悲观情绪,我无法想象编译器编写者会实现这样的事情。
编辑:保证零位的AA偏移不会对被移位的东西产生影响。
答案 4 :(得分:2)
关于arg&lt;&lt;的正确性0或arg&gt;&gt; 0,没问题,绝对没问题。
关于最终的优化: 这不会减少到&gt; nop&lt;当使用常量what = 0和/或bitcount = 0调用时,除非您将其声明为内联并选择优化(并且您选择的编译器理解内联是什么)。
所以,最重要的是,只有当参数的OR不为零时才有条件地调用函数来优化这段代码(关于我试图测试两个args都不为零的最快方法)。
答案 5 :(得分:1)
要使函数有点自我记录,您可能需要将bitCount更改为unsigned以表示负值无效的调用者。