2009-06-11 7 views
14

は、私はこのような機能を持っていると言う:ゼロビットによるビットシフトは正しく動作しますか?

inline int shift(int what, int bitCount) 
{ 
    return what >> bitCount; 
} 

毎回bitCountが非負とintのビット数の範囲内であろう別のサイトから呼び出されます。私は特に0に等しいとの呼び出しを心配しています - それは正しく動作するのでしょうか?

また、コールサイトをコンパイルするときにコンパイラが関数のコード全体を参照すると、bitCountがno-opに等しいコールが削減される可能性はありますか?

答えて

17

それは、少なくとも1つの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。あなたが..B1.1で見ることができるように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 

、Intelは "42を返す" ために "シフト(42.0)を返す" コンパイル

インテル11は、これらの2つのバリエーションのためのシフトに選び出す:シフト値は、コンパイル時に不可知である場合には

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; 

...にコンパイルあなたがそれらをポップしたときにスタック上の同じではないかもしれません、まあ、この欠落した最適化はおそらくあなたの問題の最小です。

4

広く使用されているアーキテクチャ(x86、PPC、ARMの場合は保証可能)で正しく動作します。関数がインライン化されていない限り、コンパイラはそれをnoopに減らすことはできません。

+0

これはコンパイラ固有のものであり、標準とは*関数呼び出しを作成する必要があるとは思われません。 bitCountが定数0、場合によっては0を含む変数に渡された場合、グローバル最適化は非常に簡単に関数呼び出しを呼び出す可能性があります(少なくとも変数を調べるコードがあるので、no-opではありません) 。それはコンパイラに完全に依存します。 – paxdiablo

+0

もちろん、私は野生のコンパイラがそのレベルの最適化をしたことは一度も見たことがありません - 私は投資収益率がそれを保証するには不十分だと考えています。 ---- int(int x、int shift) { if(shift> 0){/ *インライン展開を禁止する大きなブロック(デフォルトのリリース設定): – paxdiablo

+0

Pax、VC9は、 * /} return x << shift; } ---- 別の翻訳単位から呼び出しても、shift = 0を渡すと、呼び出しと>>が間引かれます。すごく印象的! – peterchen

2

argの正しさについて< < 0またはarg >> 0、問題なく、一切問題ありません。最終的な最適化について

:あなたはインラインとして宣言し、最適化(および選択のあなたのコンパイラを選択しない限り、0および/またはBITCOUNT = 0を=何定数と呼ばれたとき これは> NOP <に縮小されることはありませんインラインが何であるか理解している)。

したがって、引数のORが0でない場合にのみ条件付きで関数を呼び出すことで、このコードを最適化します(両方の引数が非ゼロであることをテストする最も速い方法)。

+0

@ jpinto3912、私は質問がシフトがゼロであるときに何が起こるかを尋ねていると思います。 – paxdiablo

+0

彼はゼロをシフトしていない、彼はゼロ何かをシフトしている。 –

+0

@Pax、@Neil:ゼロをシフトしている場合、効果的にノーオペレーションを得ることもできます。質問は1件について質問し、回答は両方の場合に注意します。 – Stobor

3

コンパイラは、コンパイル時にbitCount値がゼロであることが分かっていた場合にのみ、この最適化を実行できます。これは、渡されたパラメータが一定でなければならないであろうことを意味する:

const int N = 0; 
int x = shift(123, N); 

C++は、確かにこのような最適化を行うことができますが、私はそうする任意のコンパイラを認識していませんよ。

int x = n == 0 ? 123 : shift(123, n); 

は、ほとんどのケースでpessimisationだろうと私はそのようなことを実行するコンパイラの作家を想像することはできません。別のアプローチコンパイラがかかることがあります。

編集: 0シフトのAAシフトは、シフトされているものに影響を与えないことが保証されます。

+0

これはいいですが、ゼロでシフトすると、ノーオペレーションと同じ効果を持つことが保証されます(必ずしもコードを出す必要はありません)? – sharptooth

+0

C++は、放出されたオブジェクトコードについての推測をしておらず、ノーオペレーションの概念も持っていません。ただし、0ビットのシフトは、C++オブジェクトに影響を与えません。おそらくあなたはあなたがなぜこれについて尋ねているのかという疑問を広げることができますか? –

+0

Expanded - 何度も呼びますが、bitCountはゼロになることがあります。変数に何の効果もないことが保証されている場合は、答えに追加してください。 – sharptooth

20

According to K&R "右のオペランドが負の場合、または左の式のタイプのビットの数以上の場合、結果は不定です。" (A.7.8)したがって、>> 0はアイデンティティの右シフトであり、完全に合法です。

1

関数を多少自己文書化するには、負の値が無効であることを呼び出し元に示すためにbitCountをunsignedに変更します。

関連する問題