アセンブリにおけるC言語のstatic(静的)変数とauto(自動)変数の取り扱い方
とあるC言語のプログラムをアセンブリ言語に変換して、そこから、C言語のstaticとautoをアセンブリ言語ではどのように扱っているのか確認してみる。
C言語およびそれをmips-gccでクロスコンパイルしたプログラムは以下。
Programs in Assembly and C compiled by mips-gcc
すげーーー長くなったな笑
ということで.sの方を読み解いて確認してみる。
static
まずstaticについて。こっちは分かりやすい。
.sプログラムの最後に以下のような記述がある。
.comm _primes_stat,40
これは簡単に示すと以下とおんなじ意味。
.data .align 2 _primes_start: .space 40
データセグメント内に、_primes_startというラベルをつけて、データを40バイト確保している。
つまりこのときに長さ10の配列のために確保された40バイトは、この配列を使うためだけに確保されている。つまり プログラムの開始から終了時まで、値を保持しつづけるという性質を持つことになる。
だけどもちろん絶対上書きされないなんてことはない。関数などが再帰的に呼び出された場合は、その領域を上書きすることがある。
その領域しか使わないからこそ、再帰的な処理の時などの管理が大変そうだ。
auto
main部分にフォーカスして読んでいく。
まず64バイト分のスタックフレームを生成し、戻りアドレス$ra
とフレームポインタ$fp
を更新。
main: subu $sp,$sp,64 sw $ra,60($sp) sw $fp,56($sp)
その後フレームポインタを$spと同じアドレスを指すようにする。
上で一度$fp
を更新してもう一度ここで更新する意味がわからないけど。まぁコンパイラの仕様かな。忠実に手続き呼び出し規約には従いつつ、みたいな。
move $fp,$sp
そしたら各配列の先頭の値を準備する。
まずはstaticの方。
$v0
の値を_primes_startラベルで確保されてるデータセグメントの最初にぶっこんでることに注意。
li $v0,2 # 0x2 sw $v0,_primes_stat
そしたらauto。16($fp)
ということで、スタックフレームのデータ4個分のところに値を突っ込んでる。
なぜ16なのか。不明。
li $v0,3 # 0x3 sw $v0,16($fp)
出力する予定の文字列を読み込んで、さっき格納したstaticの配列の一番最初の値を持ってきて、出力するためのルーチンを呼ぶ。
la $a0,$LC3 lw $a1,_primes_stat jal _print_var
今度はautoでも同じことをする。
la $a0,$LC4 lw $a1,16($fp) jal _print_var
スタックを解放してmain部終了。
move $sp,$fp lw $ra,60($sp) lw $fp,56($sp) addu $sp,$sp,64 j $ra
ここからわかるように、auto変数では、static変数と違って スタックで配列を管理しているということである。
つまり、この関数が終了すれば$sp
に確保したバイト数が足され、スタックが解放され、その結果保持していたauto変数の配列もボーンと消えてしまう。
アセンブリ言語ではC言語のstaticとautoを実現しているらしい。なるほど!!
疑問点
$fp
を2回更新する理由16(fp)
とフレームポインタ(=スタックポインタ)+ 16バイト目に突っ込んでいるのか。