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に変換するパターンもあるようです。