6

コンパイラが潜在的に実行できる最適化について質問があります。

以下のコードはそれ自体を物語っています (これは一例です):

typedef struct  test
{
  short i;
}               s_test;

int function1(char *bin)
{
  s_test foo;

  lock(gmutex);
  foo.i = *(int*)bin * 8;
  unlock(gmutex);

  sleep(5);
  //
  // Here anything can happen to *bin in another thread
  // an inline example here could be: *(volatile int *)bin = 42;
  //

  int b = foo.i + sizeof(char*);

  return (b > 1000);
}

コンパイラは最後の行を

return ((*(int*)bin * 8 + sizeof(char*)) > 1000);

gcc 4.4 で -O2 または -O3 を使用した場合はそうではないように見えましたが、他のコンパイラや他のコンパイル フラグを使用した場合はそうなる可能性がありますか?

4

3 に答える 3

4

bin宣言されている型とは異なる型を読み取っているため、例は不必要に複雑です。エイリアシングのルールは非常に複雑で、char特別なものでさえあります。それについてはコメントしません。

宣言がint* bin(したがって、ポインターをキャストする必要がない)と仮定すると、コンパイラーは関数呼び出し間でステートメントを並べ替える権利を持たず、いわゆるシーケンスポイントを形成します。*binの呼び出しの前後の値はunlock異なる可能性があるため、呼び出しの後に値をロードする必要があります。

編集: slartibartfast で指摘されているように、この引数ではunlock、関数呼び出しである (または関数呼び出しを含む) ことが不可欠であり、コンパイラによって一連の操作に解決される単なるマクロではありません。

于 2012-06-05T07:45:21.013 に答える
1

私が直接の返信で言ったように、あなたのミューテックスはIMHOを何も保護していません。*bin が指す char 配列は、int アクセス中に変更される可能性があるため、マシンによっては、アクセスしたいメモリの一貫したビューを取得することさえできません。あなたの質問に戻ります: コンパイラはソース コードをあなたが想像したシーケンスに変換しませんが、実際にはソース コードと同じように動作する機械語を生成する可能性があります。関数 lock、unlock、および sleep (とにかくマクロのように見えます) を検査でき、関連するメモリ位置に副作用がなく、たとえば sleep() への呼び出しに実装定義の意味がないと推測できる場合、一時的にレンダリングします(「キャッシュ」されますが、標準では「キャッシュされません」この用語を使用) 値が無効な場合、指定したような命令シーケンスを生成する権利があります。C (C99 まで) は本質的にシングル スレッドであり、理想的な仮想 C マシンで実行されるかのようにコードが動作する限り、コンパイラは任意の最適化戦略を採用できます。Jens が言及したシーケンス ポイントは、シングル スレッド条件下での正確性には影響しません。コンパイラは、関数全体でレジスタに foo を保持するか、*bin が指すメモリ位置で foo.i をエイリアスすることさえできます。したがって、このコード本質的に危険です (ただし、ほとんどのコンパイラではこの動作は見られないと思います)。C (C99 まで) は本質的にシングル スレッドであり、理想的な仮想 C マシンで実行されるかのようにコードが動作する限り、コンパイラは任意の最適化戦略を採用できます。Jens が言及したシーケンス ポイントは、シングル スレッド条件下での正確性には影響しません。コンパイラは、関数全体でレジスタに foo を保持するか、*bin が指すメモリ位置で foo.i をエイリアスすることさえできます。したがって、このコード本質的に危険です (ただし、ほとんどのコンパイラではこの動作は見られないと思います)。C (C99 まで) は本質的にシングル スレッドであり、理想的な仮想 C マシンで実行されるかのようにコードが動作する限り、コンパイラは任意の最適化戦略を採用できます。Jens が言及したシーケンス ポイントは、シングル スレッド条件下での正確性には影響しません。コンパイラは、関数全体でレジスタに foo を保持するか、*bin が指すメモリ位置で foo.i をエイリアスすることさえできます。したがって、このコード本質的に危険です (ただし、ほとんどのコンパイラではこの動作は見られないと思います)。

于 2012-06-05T08:38:51.737 に答える