メインコンテンツまでスキップ

スタックとローカル変数

メソッドを呼び出すと,その呼び出し用のフレームが作られます。フレームには,計算中の値を置くオペランドスタックと,引数や途中結果を保存するローカル変数配列があります。

スタックから値を取り出して計算する

スタックは最後に積んだ値から取り出します。次の表では右端を TOP とします。

public static subtract(II)I {
iload_0
iload_1
isub
ireturn
}

引数を ab とすると,状態は次のように変わります。

命令実行前実行後
iload_0a
iload_1aa, b
isuba, ba - b
ireturna - b呼び出し元へ値を返し,このフレームを破棄

isub は先に積んだ値から TOP の値を引きます。加算では順序を入れ替えても結果は同じですが,減算や除算では順序が結果に影響します。

ireturn の後に「このメソッドの空のスタック」が残るわけではありません。実行中のフレームは破棄され,呼び出し元のスタックに戻り値が積まれます。

ローカル変数は番号で指定する

iload_0 はスロット 0 の int をスタックへコピーします。istore_1 はスタックの TOP を取り出してスロット 1 に保存します。ロードしてもローカル変数の値は消えません。

メソッドスロットの配置
static add(II)I0 に第1引数,1 に第2引数
add(II)I0 に this,1 に第1引数,2 に第2引数
static mix(JI)I0–1 に long 引数,2 に int 引数
mix(JI)I0 に this,1–2 に long 引数,3 に int 引数

longdouble は連続する二つのローカル変数スロットを使います。読み書きには先頭の番号を指定します。それ以外の引数は一つのスロットを使います。

this と引数以外のスロットは,読み出す前に値を保存してください。Java のフィールドや配列要素とは異なり,未設定のローカル変数を 0 として読めるわけではありません。

名前を付けて参照する

public static ten()I {
iconst_0
istore_0 [I -> count]
iinc 0 10
iload count
ireturn
}

[I -> count] はスロットに型と名前の情報を添える記法です。後続の iload count は,対応するスロットを参照します。実行時の保存先が名前付きの領域に変わるわけではありません。

一つの値と一つのスロットを区別する

スタック操作では,longdouble をカテゴリ2,それ以外の通常の値をカテゴリ1として扱います。カテゴリ2の値はスタックの深さの計算では二単位ですが,一つの値です。

命令使用できる値
dupTOP のカテゴリ1の値を複製
dup2TOP のカテゴリ2の値一つ,またはカテゴリ1の値二つを複製
swapTOP 側のカテゴリ1の値二つを交換
popTOP のカテゴリ1の値を破棄
pop2TOP のカテゴリ2の値一つ,またはカテゴリ1の値二つを破棄

たとえば double の戻り値を使わない場合は pop2 を使います。pop で半分だけ捨てることはできません。

Jaspera ではブレークポイントで停止してフレームを確認できます。分岐で状態が合わない場合は StackMapFrame と検証 を参照してください。