8

グローバルスコープで宣言したとしましょう:

const int a =0x93191;

メイン関数には、次の条件があります。

if(a>0)
    do_something

私が気づいた厄介なことは、RVDSコンパイラがifステートメントを削除し、オブジェクト ファイルにブランチ/jmp がないことです。

しかし、私が書くと:

if(*(&a)>0)
    do_something

if (cmpおよびbranch) は、コンパイルされたオブジェクト ファイルに含まれます。


対照的に、 do は (またはまたは)GCC を使用して両方を最適化します。-O1-O2-O3

#include <stdio.h>
const a = 3333;

int main()
{
    if (a >333)
        printf("first\n");

return 0;
}

-O3 でコンパイル:

(gdb) disassemble main
Dump of assembler code for function main:
0x0000000100000f10 <main+0>:    push   %rbp
0x0000000100000f11 <main+1>:    mov    %rsp,%rbp
0x0000000100000f14 <main+4>:    lea    0x3d(%rip),%rdi        # 0x100000f58
0x0000000100000f1b <main+11>:   callq  0x100000f2a <dyld_stub_puts>
0x0000000100000f20 <main+16>:   xor    %eax,%eax
0x0000000100000f22 <main+18>:   pop    %rbp
0x0000000100000f23 <main+19>:   retq   
End of assembler dump.

そして、

#include <stdio.h>
const a = 3333;

int main()
{
        if (*(&a) >333)
                printf("first\n");

return 0;
}

あげる:

(gdb) disassemble main
Dump of assembler code for function main:
0x0000000100000f10 <main+0>:    push   %rbp
0x0000000100000f11 <main+1>:    mov    %rsp,%rbp
0x0000000100000f14 <main+4>:    lea    0x3d(%rip),%rdi        # 0x100000f58
0x0000000100000f1b <main+11>:   callq  0x100000f2a <dyld_stub_puts>
0x0000000100000f20 <main+16>:   xor    %eax,%eax
0x0000000100000f22 <main+18>:   pop    %rbp
0x0000000100000f23 <main+19>:   retq   
End of assembler dump.

GCC は両方を同じように (あるべきように) 扱いますが、RVDS はそうではありませんか?


私は使用の影響を調べようとしvolatileましたが、RVDS では the を削除しましたif(a>333)が、gcc は削除しませんでした:

#include <stdio.h>
volatile const a = 3333;

int main()
{
    if (a >333)
        printf("first\n");

return 0;
}

(gdb) disassemble main
Dump of assembler code for function main:
0x0000000100000f10 <main+0>:    push   %rbp
0x0000000100000f11 <main+1>:    mov    %rsp,%rbp
0x0000000100000f14 <main+4>:    cmpl   $0x14e,0x12a(%rip)        # 0x100001048 <a>
0x0000000100000f1e <main+14>:   jl     0x100000f2c <main+28>
0x0000000100000f20 <main+16>:   lea    0x39(%rip),%rdi        # 0x100000f60
0x0000000100000f27 <main+23>:   callq  0x100000f36 <dyld_stub_puts>
0x0000000100000f2c <main+28>:   xor    %eax,%eax
0x0000000100000f2e <main+30>:   pop    %rbp
0x0000000100000f2f <main+31>:   retq   
End of assembler dump.

おそらく、私が使用した RVDS のコンパイラ バージョンにはいくつかのバグがあります。

4

2 に答える 2

11

コンパイラが「これは実際の値が何であるかを理解できるか」を判断するために通過する複雑さのレベルは無制限ではありません。十分に複雑なステートメントを記述した場合、コンパイラは単純に「値が何かわかりません。それを計算するコードを生成します」と言うだけです。

これは、コンパイラが変更されないことを完全に理解する可能性があります。しかし、一部のコンパイラがプロセスを「あきらめる」可能性もあります。これは、コンパイルチェーンのどこでこの分析が行われるかにも依存する場合があります。

これはおそらく「as-if」ルールのかなり典型的な例です。コンパイラは、これが実行された「as-if」結果を生成する最適化を実行できます。

そうは言っても、これはかなり些細なことであるはずです(コメントによると、コンパイラは*(&a)と同じように考える必要がありますa)。そのため、比較を取り除かないのは奇妙に思えます。

于 2013-06-20T17:18:17.197 に答える
4

最適化は、コンパイラの実装の詳細です。それらを実装するには時間と労力がかかり、コンパイラの作成者は通常、言語の一般的な使用方法に集中します (つまり、非常にまれなコードの最適化への投資の見返りはほとんどありません)。

どちらのコードにも重要な違いがあると言われています。最初のケースaでは、odr は使用されず、右辺値としてのみ使用されます。つまり、コンパイル時定数として処理できることを意味します。つまり、aが直接使用された場合 (アドレスオブも参照もバインドされていない)、コンパイラはすぐに値 in を置き換えます。値は、定数式が使用されるコンテキストで使用できるため、変数にアクセスせずにコンパイラによって認識される必要があります。必要です (つまり、配列のサイズを定義します)。

2 番目のケースaは、odr-used で、アドレスが取得され、その場所の値が読み取られます。コンパイラは、結果をオプティマイザに渡す前に、これらの手順を実行するコードを生成する必要があります。オプティマイザーは、それが定数であることを検出し、操作全体を値で置き換えることができますが、これは、コンパイラー自体が値を入力した前のケースよりも少し複雑です。

于 2013-06-20T19:14:45.207 に答える