スイッチ ケース アセンブリ レベル コード



Cygwin WindowsでCをプログラミングしています。少し C プログラミングを行い、言語に慣れた後、内部を見て、私が書いたコードに対してコンパイラが何をしているかを確認したいと思いました。


そこで、switch case ステートメントを含むコード ブロックを書き留め、以下を使用してそれらをアセンブリに変換しました:


gcc -S foo.c  

C ソースは次のとおりです:


switch(i)
{
case 1:
{
printf("Case 1\n");
break;
}
case 2:
{ printf("Case 2\n");
break;
}
case 3:
{
printf("Case 3\n");
break;
}
case 4:
{
printf("Case 4\n");
break;
}
case 5:
{
printf("Case 5\n");
break;
}
case 6:
{
printf("Case 6\n");
break;
}
case 7:
{
printf("Case 7\n");
break;
}
case 8:
{
printf("Case 8\n");
break;
}
case 9:
{
printf("Case 9\n");
break;
}
case 10:
{
printf("Case 10\n");
break;
}
default:
{
printf("Nothing\n");
break;
}
}

同じ結果のアセンブリは次のようになります:


movl    $5, -4(%ebp)
cmpl $10, -4(%ebp)
ja L13
movl -4(%ebp), %eax
sall $2, %eax
movl L14(%eax), %eax
jmp *%eax
.section .rdata,"dr"
.align 4
L14:
.long L13
.long L3
.long L4
.long L5
.long L6
.long L7
.long L8
.long L9
.long L10
.long L11
.long L12
.text
L3:
movl $LC0, (%esp)
call _printf
jmp L2
L4:
movl $LC1, (%esp)
call _printf
jmp L2
L5:
movl $LC2, (%esp)
call _printf
jmp L2
L6:
movl $LC3, (%esp)
call _printf
jmp L2
L7:
movl $LC4, (%esp)
call _printf
jmp L2
L8:
movl $LC5, (%esp)
call _printf
jmp L2
L9:
movl $LC6, (%esp)
call _printf
jmp L2
L10:
movl $LC7, (%esp)
call _printf
jmp L2
L11:
movl $LC8, (%esp)
call _printf
jmp L2
L12:
movl $LC9, (%esp)
call _printf
jmp L2
L13:
movl $LC10, (%esp)
call _printf
L2:

現在、アセンブリでは、コードは最初に最後のケース (つまり、ケース 10) を最初にチェックしています。これは非常に奇妙です。そして、「i」を「eax」にコピーして、私を超えたことをしています。


コンパイラが switch..case のジャンプ テーブルを実装していると聞きました。それはこのコードがしていることですか?または、それは何をしていて、なぜですか?ケースの数が少ない場合、
コードは if...else ラダー用に生成されたものと非常に似ていますが、ケースの数が増えると、この変わった実装が見られます。


よろしくお願いします。


答え:


最初に、コードは i を 10 と比較し、値が 10 より大きい場合はデフォルトのケースにジャンプします (cmpl $10, -4(%ebp) 続いて ja L13 ).


コードの次のビットは、入力を左に 2 シフトします (sall $2, %eax ) これは、ジャンプ テーブルにオフセットを生成する 4 の倍数と同じです (テーブルの各エントリは 4 バイト長であるため)


次に、ジャンプ テーブル (movl L14(%eax), %eax) からアドレスをロードします。 ) にジャンプします (jmp *%eax ).


ジャンプ テーブルは単なるアドレスのリストです (アセンブリ コードではラベルで表されます):


L14:
.long L13
.long L3
.long L4
...

L13 という点に注意してください。 デフォルトのケースを表します。これは、ジャンプ テーブルの最初のエントリ (i が 0 の場合) であり、最初に特別に処理されます (i> 10 の場合)。