21

次のような関数があるとします。

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

毎回異なるサイトから呼び出され、bitCount負でなく、 のビット数の範囲内になりますint。私は特にbitCountゼロに等しい呼び出しについて心配しています-それは正しく動作しますか?

また、呼び出しサイトをコンパイルするときに関数のコード全体を確認しているコンパイラがbitCountゼロに等しい呼び出しをノーオペレーションに減らす可能性はありますか?

4

6 に答える 6

40

K&R によると、「右のオペランドが負の場合、または左の式の型のビット数以上の場合、結果は未定義です。」(A.7.8) したがって>> 0、恒等右シフトであり、完全に合法です。

于 2009-06-11T12:31:41.497 に答える
18

少なくとも 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.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 は、次の 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;

...しかし、少なくともインライン化されているため、呼び出しのオーバーヘッドはありません。

ボーナス アセンブリ: volatile は高価です。起源 ...

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;

... したがって、スタックにプッシュする値がポップするときに同じではない可能性があるマシンで作業している場合、この最適化の失敗はおそらく最も問題が少ないでしょう。

于 2009-06-11T12:43:51.547 に答える
4

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

于 2009-06-11T11:24:48.287 に答える
3

arg << 0 または arg >> 0 の正しさについては、問題ありません。まったく問題ありません。

最終的な最適化について: 定数 what=0 および/または bitcount=0 で呼び出された場合、インラインとして宣言して最適化を選択しない限り (そして選択したコンパイラがインラインとは何かを理解している場合を除き)、これは >nop< に縮小されません。 .

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

于 2009-06-11T11:35:04.020 に答える
3

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

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

C++ では確かにそのような最適化を実行できますが、そうするコンパイラは知りません。コンパイラが取ることができる別のアプローチ:

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

ほとんどの場合、悲観的になり、コンパイラライターがそのようなことを実装することは想像できません。

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

于 2009-06-11T11:50:48.023 に答える
1

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

于 2009-06-11T15:33:12.323 に答える