21

渡されたメモリバッファが二度と使用されないことがわかっている場合、コンパイラは呼び出しを自由に削除できることをここで読みました。memsetそんなことがあるものか?(コア言語の観点からは)memset単なる通常の関数であり、コンパイラーには、その内部で何が起こっても副作用がないと想定する権利はないように思われます。

リンクされた記事では、Visual C++10がどのように削除されたかを示していますmemset。Microsoftコンパイラが標準への準拠をリードしていないことを知っているので、質問します-それは標準に準拠しているのですか、それとも単なるmsvc-ismですか?標準に従っている場合は、詳しく説明してください;)

編集: @Cubbi

次のコード:

void testIt(){
    char foo[1234];
    for (int i=0; i<1233; i++){
        foo[i] = rand()%('Z'-'A'+1)+'A';
    }
    foo[1233]=0;
    printf(foo);
    memset(foo, 0, 1234);
}

行付きのmingwの下でコンパイル:

g++ -c -O2 -frtti -fexceptions -mthreads -Wall -DUNICODE -o main.o main.cpp
g++ -Wl,-s -Wl,-subsystem,console -mthreads -o main.exe main.o
objdump -d -M intel -S main.exe > dump.asm

出力を与えた:

 4013b0:    55                      push   ebp
 4013b1:    89 e5                   mov    ebp,esp
 4013b3:    57                      push   edi
 4013b4:    56                      push   esi
 4013b5:    53                      push   ebx
 4013b6:    81 ec fc 04 00 00       sub    esp,0x4fc
 4013bc:    31 db                   xor    ebx,ebx
 4013be:    8d b5 16 fb ff ff       lea    esi,[ebp-0x4ea]
 4013c4:    bf 1a 00 00 00          mov    edi,0x1a
 4013c9:    8d 76 00                lea    esi,[esi+0x0]
 4013cc:    e8 6f 02 00 00          call   0x401640
 4013d1:    99                      cdq    
 4013d2:    f7 ff                   idiv   edi
 4013d4:    83 c2 41                add    edx,0x41
 4013d7:    88 14 1e                mov    BYTE PTR [esi+ebx*1],dl
 4013da:    43                      inc    ebx
 4013db:    81 fb d1 04 00 00       cmp    ebx,0x4d1
 4013e1:    75 e9                   jne    0x4013cc
 4013e3:    c6 45 e7 00             mov    BYTE PTR [ebp-0x19],0x0
 4013e7:    89 34 24                mov    DWORD PTR [esp],esi
 4013ea:    e8 59 02 00 00          call   0x401648
 4013ef:    81 c4 fc 04 00 00       add    esp,0x4fc
 4013f5:    5b                      pop    ebx
 4013f6:    5e                      pop    esi
 4013f7:    5f                      pop    edi
 4013f8:    c9                      leave  
 4013f9:    c3                      ret   

4013ea行目にはmemset呼び出しがあるため、mingwはそれを削除していません。mingwは実際にはWindowsスキンのGCCであるため、GCCも同じように動作すると思います。Linuxで再起動するときに確認します。

まだそのようなコンパイラを見つけるのに問題がありますか?

EDIT2:

GCCについて知りました__attribute__ ((pure))。したがって、コンパイラがmemsetについて特別なことを知っていて、それを削除するのではなく、ヘッダーで許可されているだけです-それを使用するプログラマーもそれを見る必要があります;)私のmingwはmemset宣言にこの属性を持っていないため、何があっても組み立て-私が期待するように。これを調査する必要があります。

4

1 に答える 1

12

「コンパイラーには、内部で何が起こっても副作用がないと仮定する権利はありません。」

そのとおりです。しかし、コンパイラが内部で実際に何が起こっているかを実際に知っていて、実際に副作用ないと判断できる場合、仮定は必要ありません。

これは、ほぼすべてのコンパイラの最適化がどのように機能するかです。コードには「X」と書かれています。コンパイラは、「Y」が真の場合、コード「X」をコード「Z」に置き換えることができ、検出可能な違いはないと判断します。「Y」が真であると判断し、「X」を「Z」に置き換えます。

例えば:

void func()
{
  int j = 2;
  foo();
  if (j == 2) bar();
   else baz();
}

コンパイラは、これを に最適化できますfoo(); bar();。コンパイラはfoo、 が の値を合法的に変更できないことを確認できますj。どういうわけか魔法のように がスタックのどこにあるかをfoo()把握してjそれを変更すると、最適化によってコードの動作が変更されますが、それは「魔法」を使用したプログラマーの責任です。

void func()
{
  int j = 2;
  foo(&j);
  if (j == 2) bar();
   else baz();
}

魔法を使わずfooに の値を合法的に変更できるため、今はできません。j(コンパイラが内部を参照できないと仮定すると、foo場合によっては可能です。)

「魔法」を行うと、コンパイラはコードを壊す最適化を行うことができます。ルールを守り、魔法を使わないでください。

あなたがリンクした例では、コードは、決してアクセスされず、すぐに存在しなくなる変数に特定の値を入れようとするコンパイラに依存しています。コンパイラは、コードの操作に影響を与えないことを行う必要はありません。

コードに影響を与える可能性のある唯一の方法は、スタックの未割り当て部分を覗いた場合、または以前の値を持つスタック上の新しい割り当てに依存した場合です。コンパイラにそれを要求すると、ローカル変数をレジスタに置き換えるなど、膨大な数の最適化が不可能になります。

于 2013-03-21T02:40:02.623 に答える