假设我有这样的功能:
inline int shift( int what, int bitCount )
{
return what >> bitCount;
}
每次从不同站点调用它bitCount
都是非负的,并且在int
. 我特别关心bitCount
等于零的呼叫 - 那么它会正常工作吗?
编译器在编译其调用站点时看到函数的整个代码是否有可能将bitCount
等于零的调用减少为无操作?
假设我有这样的功能:
inline int shift( int what, int bitCount )
{
return what >> bitCount;
}
每次从不同站点调用它bitCount
都是非负的,并且在int
. 我特别关心bitCount
等于零的呼叫 - 那么它会正常工作吗?
编译器在编译其调用站点时看到函数的整个代码是否有可能将bitCount
等于零的调用减少为无操作?
根据 K&R, “如果右操作数为负数,或者大于或等于左表达式类型中的位数,则结果未定义。” (A.7.8) 因此>> 0
是身份右移并且完全合法。
可以肯定的是,至少有一个 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 中看到的,英特尔将“return shift(42,0)”编译为“return 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;
...因此,如果您在一台机器上工作,当您弹出它们时,您压入堆栈的值可能不一样,那么,这种错过的优化可能是您遇到的最少的麻烦。
它可以在任何广泛使用的架构上正常工作(我可以保证 x86、PPC、ARM)。除非函数被内联,否则编译器将无法将其减少为 noop。
关于 arg << 0 或 arg >> 0 的正确性,没问题,绝对没问题。
关于最终的优化:当使用常量 what=0 和/或 bitcount=0 调用时,这不会简化为 >nop<,除非您将其声明为内联并选择优化(并且您选择的编译器了解内联是什么) .
因此,最重要的是,仅当参数的 OR 不为零时才通过有条件地调用函数来优化此代码(大约是我认为测试两个参数都非零的最快方法)。
如果编译器在编译时知道 bitCount 值为零,则编译器只能执行此优化。这意味着传递的参数必须是一个常量:
const int N = 0;
int x = shift( 123, N );
C++ 当然允许执行这样的优化,但我不知道有任何编译器这样做。编译器可以采取的替代方法:
int x = n == 0 ? 123 : shift( 123, n );
在大多数情况下会是一种悲观,我无法想象编译器编写者会实现这样的事情。
编辑:零位的 AA 移位保证对被移位的东西没有影响。
为了使函数有点自我记录,您可能希望将 bitCount 更改为无符号以向调用者表示负值无效。