postednote

パタヘネ p620を読んで、MIPSアセンブリでのアドレス指定について思いを馳せた

Gemini問答へのリンク https://gemini.google.com/share/e373ec3fe206

理解のダンプとGeminiによるレビューの抜粋

本文とGeminiとのやり取りで得た理解のダンプ

上記5つの理解についてのGeminiからのレビュー

  1. $gp(グローバルポインタ)と命令数

その通りです。 通常、32bitアドレスを指定するには lui + ori(または lw)の2命令が必要ですが、$gp 相対であれば lw $reg, offset($gp) の 1命令 で済みます。これがチリも積もれば大きな速度差(およびコードサイズの削減)になります。

  1. $gp の決定(コンパイラ vs リンカ)

その通りです。正確な理解です。

コンパイラ: 変数が「静的(static/global)」であることを見抜き、それらをデータセグメント(.data や .bss)にまとめます。

リンカ: 全オブジェクトファイルをまとめ上げる際、「どの変数がよく使われるか」「どの変数が小さいか」を基準に、gpから16bit( \pm 32\text{KB}$) 以内に収まる .sdata(small data)セクション にそれらを配置します。

  1. スタック領域の決定タイミング その通りです。 C99のVLAや alloca() が登場すると、関数が必要とするメモリ量は「実行時にその行を通るまで」決まりません。

  2. alloca() と $fp(フレームポインタ)

「$fp に退避」というよりは、「$fp をアンカー(基準)にする」という表現が適切です。

通常、ローカル変数は $sp 相対でアクセスしますが、alloca() で $sp が勝手に動くと、他の変数の位置(オフセット)がズレてしまいます。

そこで、関数の開始時の $sp を $fp にコピーして固定しておきます。

こうすれば、alloca() でどれだけ $sp を引き下げても、既存のローカル変数は $fp からの固定オフセットでアクセスし続けられます。

  1. VLA(可変長配列)の誤解 ここが修正ポイントです。

VLAの場合は、呼び出し側で決定できるので、呼び出し時には固定値で確保される

これは少し違います。VLAは「呼び出し側(Caller)」ではなく、「呼ばれた関数側(Callee)」の中でスタックを動的に確保するものです。

動作: 関数の中で int arr[n]; という行に到達したとき、実行時に n の値を計算し、その分だけ $sp を引き下げます。

性質: 仕組みとしては alloca() とほぼ同じです。つまり、VLAを使う関数も、スタックフレームのサイズはコンパイル時には決まりません。

呼び出し側ができること: 呼び出し側ができるのは、引数を渡すことだけです。その引数を使って「どれだけスタックを伸ばすか」を決めるのは、呼ばれた関数自身の責任になります。