2016-03-31 8 views
5

は、私はこの行を含むCプログラムをコンパイルのprintf( "%sのを\ n"、STR)プットに(STR)を最適化します出力はレジスタを設定した後printfと呼ば:-O2は<code>clang</code>の周りいじる

movq (%rcx), %rsi 
movq %rax, %rdi 
movb $0, %al 
callq _printf 

私はclang -O2でコンパイルしてみました。これが、この場合には完璧な理にかなっていますが、それは二つの質問を提起

movq (%rsi), %rdi 
callq _puts 

printf呼び出しはputs呼び出しに置き換えられました

  1. どのくらいの頻度で、関数呼び出し置換が最適化コンパイルで起きるのでしょうか?これは頻繁に起こるのですか?
  2. 自分のライブラリのコンパイラの最適化を書くことができますか?どうすればいい?
+0

だからclangはオープンソースなので、確かにできます:)もっと最適なコードを書くのは簡単ではないでしょうか? –

+1

私は知りません(または私は答えます)が、私の知る限り、この種の特殊なケーシングが適用されているのは、ほんのわずかな特定の標準ライブラリ関数です。 'memset'は、コンパイラがしばしば固定小サイズ用であるかどうかを識別し、そのような場合にインライン展開する特殊なケースです。 – ShadowRanger

+0

それは問題です。 clangのソースでlibcの最適化を行っていますか(私はclangをforkしません)か、libcのソースにありますか(私自身のライブラリに最適化を書くことができるという意味ですか? – LodeRunner

答えて

3
  1. 最適化されたコンパイルでは、どのくらいの頻度で関数呼び出しが行われますか?これは頻繁に起こるのですか?

LLVMにputsprintfを置き換える最適化は、クラスLibCallSimplifierです。ヘッダーファイルはllvm/include/llvm/Transforms/Utils/SimplifyLibCalls.hで、実装はllvm/lib/Transforms/Utils/SimplifyLibCalls.cppです。ファイルを見ると、他のライブラリ呼び出しの最適化のいくつかの例が得られます(ヘッダーファイルはおそらく開始が簡単です)。もちろんLLVMには他にも多くの最適化がありますが、LLVM passesのリストを見ればそれらのいくつかのアイデアを得ることができます。

  1. 私自身のライブラリのコンパイラの最適化を書くことができますか?どうすればいい?

はいできます。 LLVMは非常にモジュール化されており、一連のパスでIR上の変換を実行します。だから自分のライブラリにカスタムパスを追加したいのであれば、そうすることができます(ただし、LLVMコンパイラの流れの仕方を理解するにはかなりの労力が必要です)。良い出発点は文書:Writing an LLVM Passです。

+0

偉大な答え、ありがとう! – LodeRunner

-4

putsはprintfよりはるかに小さい関数であり、実行可能ファイルは通常サイズの半分です。印刷のために数値を文字列に変換する際のprintfにのみ必要とされている、あなたは(この使用itoaは行うことができます)

+0

これは、どちらの質問にも答えません –

+1

これは実際に質問に答えません。 OPはそれが合理的な最適化であることを完全に認識しているようだ。 – ShadowRanger

2

最適化のこの種は、で定義されているようprintfという名前の関数は唯一printf関数とすることができることを知って、コンパイラに依存Cの標準。プログラムがprintfを他に意味すると定義している場合、プログラムは未定義の動作を呼び出しています。これにより、標準のprintf関数が呼び出されているかのように動作する場合に、コンパイラはputsへの呼び出しを代入することができます。ユーザーが定義したprintf関数が呼び出されたかのように "あたかも"動作しているかどうか心配する必要はありません。したがって、これらの種類の関数置換最適化は、CまたはC++標準で定義された関数にかなり制限されています。コンパイラが何らかの形で与えられた標準が有効であることを何とか知っていれば他の標準もあるかもしれません。

コンパイラのソースコードを自分で修正するのではなく、コンパイラにこれらの種類の関数置換が可能です独自の機能。しかし、制限があるため、インライン関数と同様のことができます。たとえば、あなたはこのようなものでprintf/puts最適化に似たものを実現することができます。しかし、最適化を

inline int myprintf(char const *fmt, char const *arg) { 
    if (strcmp(fmt, "%s\n") == 0) { 
     return myputs(args); 
    } 
    return _myprintf_impl(fmt, arg) 
} 

fmtパラメータに基づいて呼び出すように機能し、コンパイル時に選択することができますコンパイラオンのみの場合それは定数文字列であると判断できます。それができない場合、または最適化が有効になっていない場合、コンパイラは呼び出しごとにチェックするコードを生成する必要があり、簡単にこれをペシメーゼーションに変えることができます。この最適化は、コンパイラがstrcmpの仕組みを知っていることと、呼び出しを完全に削除することに依存していることに注意してください。コンパイラが行うことができる別のライブラリ関数呼び出しの例もあります。あなたはGCCの__builtin_constant_p機能でこれを改善することができます

inline int myprintf(char const *fmt, char const *arg) { 
     if (__builtin_constant_p(fmt[0]) 
      && strcmp(fmt, "%s\n") == 0) { 
       return myputs(arg); 
     } 
     return _myprintf_impl(fmt, arg); 
} 

GCCの下で、これはフォーマット文字列に実行時間をチェックしたことがないコードになります。fmtがコンパイル時に"%s\n"であると判断できる場合は、無条件にmyputsを呼び出すコードを生成し、それ以外の場合は無条件に_myprintf_implを呼び出すコードを生成します。最適化を有効にすると、この機能は決して悲観的なものではありません。残念ながら、clangは__builtin_constant_p関数をサポートしていますが、私のclangは常に_myprintf_implを無条件に呼び出すコードを生成します。

関連する問題