2009年07月03日
gccのビルトイン関数 (printf)
gccのtipsを紹介します。主にgcc3.x からgcc4.x で変わっているところです。
#include <stdio.h> int main() { printf("Hello, world!\n"); }
このプログラムはgcc3では普通にprintfを呼び出すコードを生成しますが、gcc4ではどうなるでしょうか。ARM版のexeGCC4でちょっと試してみます。
> gcc -S hello.c
生成されたhello.s を見てみると
... .LC0: .ascii "Hello, world!\000" .text .align 2 .global main .type main, %function main: @ args = 0, pretend = 0, frame = 8 @ frame_needed = 0, uses_anonymous_args = 0 str lr, [sp, #-4]! sub sp, sp, #12 ldr r0, .L3 bl puts add sp, sp, #12 ldmfd sp!, {pc}
printfでなくてputsが呼ばれています。これはgccがprintfがどういう関数かを知っていて、
引数が一つしかなくて、なおかつその引数の文字列が改行で終わっている時はputsに
置き換えているのです。このようにgccが知っている関数をビルトイン関数と呼びます。
ビルトイン関数の最適化は、特に -O オプションをつけなくても行われます。
逆にこれをやってほしくない場合には -fno-builtin オプションをつけます。
特定のビルトイン関数だけを無効にする場合は -fno-builtin-<関数名> とします。
例えば printf のビルトイン関数を無効にするには -fno-builtin-printf です。
ちょっと試してみましょう。
> gcc -S -fno-builtin-printf hello.c
... .LC0: .ascii "Hello, world!\012\000" .text .align 2 .global main .type main, %function main: @ args = 0, pretend = 0, frame = 8 @ frame_needed = 0, uses_anonymous_args = 0 str lr, [sp, #-4]! sub sp, sp, #12 ldr r0, .L3 bl printf add sp, sp, #12 ldmfd sp!, {pc}
今度はそのままprintfが呼ばれるコードが生成されました。
プログラムをちょっと変更してみます。
#include <stdio.h> int main() { printf("Hello, world!"); printf("\n"); }
これをまたexegcc4 でコンパイルしてコードを見てみます。
> gcc -S hello2.c
... .LC0: .ascii "Hello, world!\000" .text .align 2 .global main .type main, %function main: @ args = 0, pretend = 0, frame = 8 @ frame_needed = 0, uses_anonymous_args = 0 str lr, [sp, #-4]! sub sp, sp, #12 ldr r0, .L3 bl printf mov r0, #10 bl putchar add sp, sp, #12 ldmfd sp!, {pc}
ひとつめのprintfは引数が一つですが、それが改行で終わっていないために、putsにならずにそのままprintfが呼ばれるコードになりました。
ふたつめのprintfは引数が一つで、一文字だけなのでputchar に変換されました。
このようにgcc4ではとりあえずprintfで書いておけば、コンパイラが適切に置き換えてくれます。
(追記)
このあたりのprintfの変換はgccのソースコード
gcc/builtins.c の関数fold_builtin_printf
をみるとわかります。
フォーマットが "%s\n" のときにputsに変換とか、"%c"のときにputcharに変換するパターンもあるようです。