読者です 読者をやめる 読者になる 読者になる

【アセンブリ】スタックフレームを生成するときにスタックポインタから値を引く理由【MIPS】

アセンブリ

スタックフレームを生成するときになぜ$spから値を「引く」のか分からなかったのでそれについての考察と現状の理解をまとめた記事を書いておく。

スタックポインタからフレームのサイズを引いて、フレーム用にメモリを割り当てる (p. 764)

スタックフレームは低位から高位のメモリアドレスにフーレムを生成する。$fpは最初初期値が$spと同じように設定されていて、$spから引いた数の分だけ$spと$fpの差は開き、そこがフーレムとして生成される、とか?

ここにある画像がPatterson&Hennesy本よりもすごく分かりやすかった。

http://www.swlab.cs.okayama-u.ac.jp/~nom/lect/p3/what-is-calling-convention.html

Screenshot 2014-11-23 23.59.46.png

要するに、 使う領域は白の部分だった。今までの認識では下から上にいくと思ってたから食い違ってたのだけど、そういいうことか。

だから、$spから欲しいバイト分だけ値を引けばその分ビヨーンて下に伸びて確保領域が広くなっていく、ってことなのだろう。

そして4バイトずつ計算できれば$fpはいらない。($fpは計算を簡易化するために用いるので必須ではない)

$fpの使い方がイマイチわからなかったので、それはまた別の記事で。

戻りアドレスを退避するときの20とか古いフレームポインタを対比するときの16の意味は?

また、この疑問に関しても同様にわかってくることがある。

  .text
  .globl main
main:
  subu  $sp, $sp, 32  # Length of stack frame: 32 bytes
  sw    $ra, 20($sp)  # Save return address (戻りアドレスを退避)
  sw    $fp, 16($sp)  # Save old frame pointer (古いフレームポインタを退避)
  addiu $fp, $sp, 28  # Set up frame pointer

基本のデータ量2バイトの倍語長、つまり4バイトを一つのデータにつき必要としているとしたら、全部で32バイト確保したうち、$spが32にあって戻りアドレスが20にあって古いアドレスが16に入れて、、

*追記

以下のようなコードで、下の図になるようなスタックを形成したことを考えればわかりやすい。

 1: subu $sp, $sp, 32       # スタックを32バイト確保
 2: sw   $ra, 20($sp)       # $sp + 20 のアドレスにもともとの
 3:                         # 呼出し元アドレスを保存
 4:                         # X区間で jal しなければ,不要
 5: 
 6: sw   $fp, 16($sp)       # $sp + 16 のアドレスに $fp を保存
 7: addu $fp, $sp, 28       # 新しく $fpを設定
 8: 
 9: # (この間で必要な処理) (X区間)
10: 
11: lw   $ra, 20($sp)       # 保存しておいた $sp を復元
12: lw   $fp, 16($sp)       # 保存しておいた $fp を復元
13: addu $sp, $sp, 32       # スタックを開放
14: j    $ra                # 呼出し元に戻る

Screenshot 2014-11-24 13.51.57.png

スタックの4つ分は、Cをコンパイルする過程でどうやら必要らしい。

参照:http://www.swlab.cs.okayama-u.ac.jp/~nom/lect/p3/what-is-calling-convention.html