Ответ 1
Несомненно, что хотя бы один компилятор С++ распознает ситуацию (когда значение 0 известно во время компиляции) и делает его no-op:
Источник
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
сборка Intel С++ 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, Intel компилирует "return shift (42,0)" в "return 42".
Intel 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);
}
... вместо no-op компилируется...
..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;
... так что если вы работаете на машине, где значения, которые вы нажимаете на стек, могут быть не такими же, когда вы их всплываете, ну, эта пропущенная оптимизация, скорее всего, будет наименее из ваших проблем.