GCCでswitchを使ってると、明確に確定値の場合はswitchが自動的に必要最小限の
演算に展開さていることを知った。
まずは子のCのコード
#include <stdio.h> main() { switch('a') { case 'a': printf("hoge"); case 'b': printf("fuge"); } }
みてのとおりswitch整数式中に定数 'a'を指定して,case中にも'a'の条件がある。
すると
.file "test.c" .section .rodata .LC0: .string "hoge" .LC1: .string "fuge" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %eax movq %rax, %rdi movl $0, %eax call printf movl $.LC1, %eax movq %rax, %rdi movl $0, %eax call printf leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)" .section .note.GNU-stack,"",@progbits
このようにjmp命令のないコードが出力される。「あれ比較してねぇぞ」と思ったので
テストのために以下のようなコードを
#include <stdio.h> main() { switch('b') { case 'a': printf("hoge"); case 'b': printf("fuge"); } }
こんどはbの方を指定しました。すると
.file "test.c" .section .rodata .LC0: .string "fuge" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %eax movq %rax, %rdi movl $0, %eax call printf leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)" .section .note.GNU-stack,"",@progbits
このように綺麗さっぱりaの式もhogeの文字列領域も確保されていません。
おそるべしGCC!!コンパイル時に判定可能なものは判定してしまって不必要なコードをは自動的に削除されています。
#include <stdio.h> main() { switch('a') { case 'a': printf("hoge"); break; case 'b': printf("fuge"); } }
breakを入れると..
.file "test.c" .section .rodata .LC0: .string "hoge" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %eax movq %rax, %rdi movl $0, %eax call printf nop leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)" .section .note.GNU-stack,"",@progbits
breakの分の命令は増えたもののbのための式は確実に存在しません。ちゃんとbreakも処理しているようですね。
scanfにしてちゃんと実行時にしか判定できなくすると
#include <stdio.h> main() { char c; scanf("%c", &c); switch(c) { case 'a': printf("hoge"); case 'b': printf("fuge"); } }
ときちんとcmpとjmpが出現します。
.file "test.c" .section .rodata .LC0: .string "%c" .LC1: .string "hoge" .LC2: .string "fuge" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl $.LC0, %eax leaq -1(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi movl $0, %eax call __isoc99_scanf movzbl -1(%rbp), %eax movsbl %al, %eax cmpl $97, %eax je .L3 cmpl $98, %eax je .L4 jmp .L6 .L3: movl $.LC1, %eax movq %rax, %rdi movl $0, %eax call printf .L4: movl $.LC2, %eax movq %rax, %rdi movl $0, %eax call printf .L6: leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)" .section .note.GNU-stack,"",@progbits
以上のようにGCCは適切にコンパイル時に判断して最適なコードを出力しているようです。
すごい!