2016-07-27 6 views
8

私はstrlen.cに、次のしている場合:その後、ClangのBuiltinsは組み込まれていないのですか?

int call_strlen(char *s) { 
    return __builtin_strlen(s); 
} 

そして、このようにgccと打ち鳴らすの両方でそれをコンパイルします。

gcc -c -o strlen-gcc.o strlen.c 

clang -c -o strlen-clang.o strlen.c 

私はstrlen関数-clang.oは、基準が含まれていることを見て驚いています"strlen"に変更しますが、gccは期待して関数をインライン展開し、そのような参照はありません。 (以下のobjdumpsを参照してください)。これはclangのバグですか?私は3.8を含むclangコンパイラのいくつかのバージョンでそれをテストしました。

編集:これは私にとって重要な理由は、私が-nostdlibとリンクしていることです。そしてclangコンパイルされたバージョンでは、strlenが見つからないというリンクエラーが発生します。

クラン

@> objdump -d strlen-clang.o 

strlen-clang.o:  file format elf64-x86-64 


Disassembly of section .text: 

0000000000000000 <call_strlen>: 
    0: 55     push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: 48 83 ec 10   sub $0x10,%rsp 
    8: 48 89 7d f8   mov %rdi,-0x8(%rbp) 
    c: 48 8b 7d f8   mov -0x8(%rbp),%rdi 
    10: e8 00 00 00 00  callq 15 <call_strlen+0x15> 
    15: 89 c1     mov %eax,%ecx 
    17: 89 c8     mov %ecx,%eax 
    19: 48 83 c4 10   add $0x10,%rsp 
    1d: 5d     pop %rbp 
    1e: c3     retq 


@> objdump -t strlen-clang.o 

strlen-clang.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 strlen.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 g  F .text 000000000000001f call_strlen 
0000000000000000   *UND* 0000000000000000 strlen 

GCC

@> objdump -d strlen-gcc.o 

strlen-gcc.o:  file format elf64-x86-64 


Disassembly of section .text: 

0000000000000000 <call_strlen>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: 48 89 7d f8    mov %rdi,-0x8(%rbp) 
    8: 48 8b 45 f8    mov -0x8(%rbp),%rax 
    c: 48 c7 c1 ff ff ff ff mov $0xffffffffffffffff,%rcx 
    13: 48 89 c2    mov %rax,%rdx 
    16: b8 00 00 00 00   mov $0x0,%eax 
    1b: 48 89 d7    mov %rdx,%rdi 
    1e: f2 ae     repnz scas %es:(%rdi),%al 
    20: 48 89 c8    mov %rcx,%rax 
    23: 48 f7 d0    not %rax 
    26: 48 83 e8 01    sub $0x1,%rax 
    2a: 5d      pop %rbp 
    2b: c3      retq 

@> objdump -t strlen-gcc.o 

strlen-gcc.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 strlen.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000000 g  F .text 000000000000002c call_strlen 
+1

最適化するようにコンパイラを設定していないため、clangは最適化されませんでした。ショッキング。 – EOF

+2

@EOFどのように関連性がありますか?確かに、組み込み関数を使用するように言われれば、組み込み関数を使うべきです - 「最適化モードで組み込み関数を使うのではなく、おそらくあなたが悩まされるかもしれません。 'builtin'は' inline'のようなオプションのリクエストですか?もしそうなら、それを指定するClangのドキュメントのビットに私たちをリンクさせることができますか? –

+2

@EOF FYI私は '-O3'でコンパイルしましたが、アセンブリはかなり短く、' jmp _strlen'を含んでいます。 – Siguza

答えて

4

ただ邪魔に最適化を取得する:

clang -O0で:

t.o: 
(__TEXT,__text) section 
_call_strlen: 
0000000000000000 pushq %rbp 
0000000000000001 movq %rsp, %rbp 
0000000000000004 subq $0x10, %rsp 
0000000000000008 movq %rdi, -0x8(%rbp) 
000000000000000c movq -0x8(%rbp), %rdi 
0000000000000010 callq _strlen 
0000000000000015 movl %eax, %ecx 
0000000000000017 movl %ecx, %eax 
0000000000000019 addq $0x10, %rsp 
000000000000001d popq %rbp 
000000000000001e retq 
問題の上に今 clang -O3

t.o: 
(__TEXT,__text) section 
_call_strlen: 
0000000000000000 pushq %rbp 
0000000000000001 movq %rsp, %rbp 
0000000000000004 popq %rbp 
0000000000000005 jmp _strlen 

、と

打ち鳴らすのドキュメントでは、すべてのGCC-サポート組み込みコマンドその打ち鳴らすサポートを主張します。
しかし、GCC documentationは同義語として組み込み関数とそのライブラリー同等物の名前を治療するためのようだ:

どちらの形式は(試作品を含む)同じ型を持っている、(自分のアドレスが取られる)同じアドレス、およびCライブラリ関数[...]と同じ意味です。

また、それは確かに最適化された取得する(strlenの場合のように)ライブラリ同等で組み込み関数を保証するものではありません。これらの関数の

多くは、特定の場合にのみ最適化されています。特定のケースで最適化されていない場合は、ライブラリ関数の呼び出しが発行されます。

さらに、clang internals manualは一度だけ__builtin_strlenに言及:

  • __builtin_strlenstrlen:引数が文字列リテラルである場合、これらは整数定数式として折り畳ま一定です。

それ以外のことは約束していないようです。

__builtin_strlenの引数は文字列リテラルではないので、GCCのドキュメントでは組み込み関数の呼び出しをライブラリ関数呼び出しに変換できるので、clangの動作は完全に有効と思われます。

"patch for review" on the clang developers mailing listも書かれています:

コンパイル時の評価が必要/できない場合[...]それはまだ[...]、ライブラリのstrlen関数の使用を実行時にフォールバックします。

これは2012年のことでしたが、少なくともそのときはコンパイル時の評価のみがサポートされていたことを示しています。あなたが唯一それをプログラムを自分でコンパイルして、使用および/または配布する必要がある場合、私はあなたが単にGCCを使用することをお勧め、

  • は今、私は2つのオプションを参照してください。

  • gccとclangの両方でコードをコンパイルできるように他の人が必要な場合は、静的リンクの依存関係としてCライブラリを追加することをお勧めします。

私は強くに対するは(あなたが同意しない場合は、the glibc oneと比較し、あなた自身のstrlenの実装を書いてみてください)も、一見単純な例のために、標準ライブラリ関数の独自の実装を転がり助言します。

+0

glibcはより良いパフォーマンスを目指しています:nice! –

+0

"標準ライブラリ関数の独自の実装をロールバックすることを強くお勧めします" - パフォーマンス以外の特別な理由はありますか?両方の関数を実行し、それらが同じものを出力することを確認するデバッグモードを持つことは簡単です。 – TLW

+1

@TLW仕様へのバグと適合。基本的な機能は正しいかデバッグするのが難しくありませんが、エッジの場合は見つけにくい場合があります。また、パフォーマンスを最適化しようとすると、単純な関数でも複雑になることがあります。そして、それらがユーザーの入力に触れると、[非常に多くのメモリエラーが悪用される](https://github.com/struct/mms/blob/master/Modern_Memory_Safety_In_C_CPP.pdf)。これとは対照的に、C標準ライブラリ関数は何十年もにわたって監査されてきました。 – Siguza

4

GCCもClangも、この組み込み関数をインライン化することを約束していません。 You quotedな約束作るために見せかけ、いくつかのGCCのドキュメント:

を... GCC組み込み関数は常にインライン展開されている...

が、これは、フラグメントは、文脈から引き出さ文です。 The complete sentenceは、以下に説明する標準のCライブラリ関数としてライブラリ当を有するビルトインを除いて

ある、またはそのライブラリ・コールに展開は、 GCC組み込み関数は常にインライン展開されると対応するエントリーポイントを持たず、そのアドレスを取得することができません。

__builtin_strlenは、ライブラリ同等strlenを持っているので、この文は、それがインライン化されますかどうかについては何の約束も行いません。

関連する問題