BEAM命令

この記事は英語の原文を日本語に翻訳したものです。原文: https://blog.stenmans.org/theBeamBook/#AP-Instructions

翻訳元: happi/theBeamBook 7998e22e78417dbe20e5136b9aee862a1ecaa404(コミット 7998e22

本章では、BEAM汎用命令セットの大半の命令を詳しく見ていきます。 次節では、コード内のドキュメント(lib/compiler/src/genop.tabを参照)から生成した簡単な説明とともに、すべての命令を一覧にしています。

関数とラベル

label Lbl

汎用命令セットの命令番号1は、実のところ命令と呼べるようなものではありません。 コード中の現在位置に名前、というより番号を与えるだけの、モジュールローカルなラベルです。

各ラベルはジャンプの着地点になりうるため、ベーシックブロックの開始位置を示す場合があります。

func_info Module Function Arity

各関数のコードはfunc_info命令から始まります。 この命令はfunction clauseエラーを生成するために使われ、関数の実際のコードはfunc_info命令の直後にあるラベルから実行が始まります。

ガード付きの関数を考えてみましょう。

id(I) when is_integer(I) -> I.

この関数に対応するBeamコードはおおむね次のようになります。

{function, id, 1, 4}.
  {label,3}.
    {func_info,{atom,test1},{atom,id},1}.
  {label,4}.
    {test,is_integer,{f,3},[{x,0}]}.
    return.

ここでメタ情報{function, id, 1, 4}は、id/1関数の実行がラベル4から始まることを示しています。 ラベル4ではx0に対してis_integerを行い、失敗するとfunc_info命令を指すラベル3(f3)へジャンプしてfunction clause例外を生成します。 成功した場合はそのまま処理を続け、引数(x0)を返します。

func_info命令は、メモリ上の別の場所にあるExportレコード(erts/emulator/beam/export.hで定義)を指しています。 このレコード内にあるいくつかの専用のメモリワードは、トレース機構がすべてのプロセスによる関数の呼び出し・返却のたびに発火する特別なトレース命令を配置するために使われます。

テスト命令

型テスト

型テスト命令(is_* Lbl Argument)は、引数が指定した型かどうかを調べ、そうでなければラベルLblへジャンプします。 beamの逆アセンブラは、これらの命令をすべてtest命令として包んで表示します。例えば次のようになります。

    {test,is_integer,{f,3},[{x,0}]}.

現在の型テスト命令には、is_integeris_floatis_numberis_atomis_pidis_referenceis_portis_nilis_binaryis_listis_nonempty_listis_functionis_function2is_booleanis_bitstris_tupleがあります。

さらに、アリティ3を取る型テスト命令が1つあります。test_arity Lbl Arg Arityです。 この命令は、引数(タプルであることを前提とします)のアリティがArityであるかどうかを調べます。 通常、この命令の直前にはis_tuple命令が置かれます。

比較

比較命令(is_* Lbl Arg1 Arg2)は、命令の指示に従って2つの引数を比較し、比較が成立しなければLblへジャンプします。

比較命令には、is_ltis_geis_eqis_neis_eq_exactis_ne_exactがあります。

Erlangのすべての項は順序付けられているため、これらの命令はどんな2つの項でも比較できることを思い出してください。 たとえば、アトムselfself()の返すpidより小さいかどうかを調べることもできます(実際に小さいです)。

数値については、比較がErlangのnumber型に基づいて行われることに注意してください(詳しくは型システムの章を参照してください)。 つまり、floatとintegerが混在する比較では、精度の低い方の数値がもう一方の型に変換されてから比較されます。 たとえば私の環境では、1と1.0は等しいと比較され、9999999999999999と1.0e16も同様です。 浮動小数点数の比較には常にリスクが伴うため避けるべきであり、結果は基盤となるハードウェアによって変わることがあります。

整数の1と浮動小数点数の1.0を確実に区別して比較したい場合は、is_eq_exactとis_ne_exactを使います。 これはErlangの演算子=:==/=に対応します。

関数呼び出し

本章では、さまざまな呼び出し命令が何を行うのかを概観します。 関数呼び出しの仕組みについての詳しい説明は、呼び出しの章を参照してください。

call Arity Label

同一モジュール内、ラベルLabelにあるアリティArityの関数を呼び出します。 まずリダクションカウントを減らし、必要であればコンテキストスイッチを行います。 呼び出し後の現在のコードアドレスはCPに保存されます。

ローカル呼び出しでは、指定するラベルは常にそのコードが開始する関数の2番目のラベルです。 コンテキストスイッチが必要になった際にMFAを取得できるよう、そのラベルの直前の命令はfunc_infoであることが前提とされています。

call_only Arity Label

同一モジュール内、ラベルLabelにあるアリティArityの関数へ末尾再帰呼び出しを行います。 まずリダクションカウントを減らし、必要であればコンテキストスイッチを行います。 CPは戻りアドレスで更新されません。

call_last Arity Label Deallocate

スタックをDeallocateワード分解放したうえで、同一モジュール内、ラベルLabelにあるアリティArityの関数へ末尾再帰呼び出しを行います。 まずリダクションカウントを減らし、必要であればコンテキストスイッチを行います。 CPは戻りアドレスで更新されません。

call_ext Arity Destination

Destinationで指定される、アリティArityの関数へ外部呼び出しを行います。 アセンブリ上でDestinationは通常{extfunc, Module, Function, Arity}のように書かれ、これはモジュールのimportsセクションに追加されます。 まずリダクションカウントを減らし、必要であればコンテキストスイッチを行います。 CPは戻りアドレスで更新されます。

call_ext_only Arity Destination

Destinationで指定される、アリティArityの関数へ末尾再帰的な外部呼び出しを行います。 アセンブリ上でDestinationは通常{extfunc, Module, Function, Arity}のように書かれます。 まずリダクションカウントを減らし、必要であればコンテキストスイッチを行います。 CPは戻りアドレスで更新されません。

call_ext_last Arity Destination Deallocate

スタックをDeallocateワード分解放したうえで、Destinationで指定される、アリティArityの関数へ末尾再帰的な外部呼び出しを行います。 アセンブリ上でDestinationは通常{extfunc, Module, Function, Arity}のように書かれます。 まずリダクションカウントを減らし、必要であればコンテキストスイッチを行います。 CPは戻りアドレスで更新されません。

bif0 Bif Reg, bif[1,2] Lbl Bif [Arg,…​] Reg

与えられた引数でbifBifを呼び出し、結果をRegに格納します。 bifが失敗した場合はLblへジャンプします。 アリティ0のbifは失敗しえないため、bif0には失敗時のラベルがありません。

これらの命令で呼び出されるbifは、ヒープ上への確保やガベージコレクションの引き金を引くことはできません。 それらが必要な場合はgc_bifを参照してください。

gc_bif[1-3] Lbl Live Bif [Arg, …​] Reg

与えられた引数でbifBifを呼び出し、結果をRegに格納します。 bifが失敗した場合はLblへジャンプします。 引数はx(Live)x(Live+1)x(Live+2)に格納されます。

この命令は引数Liveを持つため、ガベージコレクションを起動するのに十分な情報が得られます。

call_fun Arity

call_fun命令は、引数が先頭のArity個の引数レジスタに置かれ、fun(クロージャへのポインタ)が最後の引数の次のレジスタx[Arity+1]に置かれていることを前提とします。

つまり、アリティ0の呼び出しではクロージャはx[0]に置かれます。 アリティ1の呼び出しではx[0]に引数が、x[1]にクロージャが入ります。以下同様です。

アリティが関数オブジェクトと一致しない場合はbadarityを送出します。 関数でない値が渡された場合はbadfunを送出します。

apply Arity

X registersに格納されたArity個の引数で関数呼び出しをapplyします。 モジュールを表すアトムはx[Arity]に、関数を表すアトムはx[Arity+1]に格納されます。 モジュールはタプルで表現されることもあります。

apply_last Arity Dealloc

CPをポップし、要素を解放してからCPを再びプッシュするという手順で、スタック上のDealloc個の要素を解放します。 続いて、新しい場所へジャンプすることで、X registersに格納されたArity個の引数を使って末尾再帰呼び出しを行います。 モジュールと関数を表すアトムは、それぞれx[Arity]x[Arity+1]に格納されます。 モジュールはタプルで表現されることもあります。

スタックとヒープの管理

BeamにおけるErlangプロセスのスタックとヒープは同じメモリ領域を共有しています(詳しい説明はプロセスの章メモリの章を参照してください)。 スタックはアドレスの低い方向へ、ヒープはアドレスの高い方向へ伸びていきます。 スタックかヒープのどちらかで利用可能な量を超える空間が必要になると、Beamはガベージコレクションを行います。

  • リーフ関数:他の関数を一切呼び出さない関数のことです。
  • 非リーフ関数:他の関数を呼び出す可能性がある関数のことです。

これらの命令は、非リーフ関数が現在のスタックフレームを構築・解体するためにも使われます。 つまり、関数に入るときに継続ポインタ(CP)がスタックに保存され、抜けるときにスタックから読み戻されます。

リーフ関数の骨格は次のようになります。

{function, Name, Arity, StartLabel}.
  {label,L1}.
    {func_info,{atom,Module},{atom,Name},Arity}.
  {label,L2}.
    ...
    return.

非リーフ関数の骨格は次のようになります。

{function, Name, Arity, StartLabel}.
  {label,L1}.
    {func_info,{atom,Module},{atom,Name},Arity}.
  {label,L2}.
    {allocate,Need,Live}.

    ...
    call ...
    ...

    {deallocate,Need}.
    return.

allocate StackNeed Live

継続ポインタ(CP)を保存し、スタック上にStackNeedワード分の領域を追加で確保します。 確保の途中でメモリが尽きた場合はGCを呼び出し、先頭のLive個のxレジスタがルートセットの一部を構成します。 たとえばLiveが2であれば、GCはレジスタX0とX1を保存し、残りは未使用として解放されます。

スタック上に確保する際は、スタックポインタ(E)が減少します。

allocateの実行例(Allocate 1 0)

       Before           After
         | xxx |            | xxx |
    E -> | xxx |            | xxx |
         |     |            | ??? | caller save slot
           ...         E -> | CP  |
           ...                ...
 HTOP -> |     |    HTOP -> |     |
         | xxx |            | xxx |

allocate_heap StackNeed HeapNeed Live

継続ポインタ(CP)を保存し、スタック上にStackNeedワード分の領域を追加で確保します。 さらに、ヒープ上にもHeapNeedワード分の空きがあることを保証します。 確保の途中でメモリが尽きた場合は、保持すべきXレジスタの数をLiveとしてGCを呼び出します。

ヒープポインタ(HTOP)は、実際にヒープの確保が行われるまで変更されません。

allocate_zero StackNeed Live

この命令はallocateと同様に動作しますが、確保したスタックスロットをNILでクリアする点が異なります。

allocate_zeroの実行例(allocate_zero 1 0)

       Before           After
         | xxx |            | xxx |
    E -> | xxx |            | xxx |
         |     |            | NIL | caller save slot
           ...         E -> | CP  |
           ...                ...
 HTOP -> |     |    HTOP -> |     |
         | xxx |            | xxx |

allocate_heap_zero StackNeed HeapNeed Live

allocate_heap_zero命令はallocate_heap命令と同様に動作しますが、確保したスタックスロットをNILでクリアする点が異なります。

test_heap HeapNeed Live

test_heap命令は、ヒープ上にHeapNeedワード分の空きがあることを保証します。 確保の途中でメモリが尽きた場合は、保持すべきXレジスタの数をLiveとしてGCを呼び出します。

init N

init命令は、CPポインタより上のNワード分のスタックにNILを書き込んでクリアします。

deallocate N

deallocate命令はallocateの逆の操作を行います。 CP(継続ポインタ)を復元し、N+1ワード分のスタックを解放します。

return

return命令は、継続ポインタ(CP)に入っているアドレスへジャンプします。 CPの値はC言語における0にあたる値に設定されます。

trim N Remaining

CPを一時変数へポップし、スタックのNワード分を解放してから、CPをスタックの先頭に戻します。 (引数Remainingは、私が知るかぎり使われていません。)

trimの実行例(Trim 2)

       Before           After
         | ??? |            | ??? |
         | xxx |       E -> | CP  |
         | xxx |            | ... |
    E -> | CP  |            | ... |
         |     |            | ... |
           ...                ...
 HTOP -> |     |    HTOP -> |     |
         | xxx |            | xxx |

データの移動、抽出、書き換え

move Source Destination

移動元Source(リテラルまたはレジスタ)の値を、移動先レジスタDestinationへ移動します。

get_list Source Head Tail

リストセルを分解する操作です。 Sourceで指定されたリスト(consセル)のheadとtail(car部とcdr部)を取り出し、レジスタHeadTailに格納します。

get_tuple_element Source Element Destination

配列の添字読み出し操作です。 Sourceタプルの位置Elementにある要素を取り出し、レジスタDestinationに格納します。

set_tuple_element NewElement Tuple Position

配列の添字を使った破壊的更新操作です。 TuplePositionにある要素を、新しい値NewElementで更新します。

項の構築

put_list Head Tail Destination

ヒープ上に新しいリスト(cons)セルを構築し(2ワード)、そのアドレスをレジスタDestinationに格納します。 リストセルの第1要素にはHeadの値が、第2要素にはTailの値が設定されます。

put_tuple Size Destination

ヒープ上に空のタプルを構築し(Size+1ワード)、そのアドレスをレジスタDestinationに格納します。 この時点では要素は設定されません。 put_tuple命令の後には必ず複数のput命令が続き、要素を1つずつ破壊的に設定していきます。

put Value

直前のput_tuple命令で作られたタプルの次の要素へ、Valueを破壊的に格納します。 書き込みアドレスはVM内部で保持・インクリメントされます。 新しいタプルの内容を設定するには、複数のput命令が使われます。

make_fun2 LambdaIndex

モジュールのLambdaテーブル上のインデックスによって定義される関数オブジェクトを生成します。 Lambdaテーブルは、エントリポイント(ラベルまたはexportエントリ)、アリティ、そしてfrozen変数をいくつ取るかを定義します。 frozen変数の値は現在の実行コンテキスト(Xレジスタ)からコピーされ、関数オブジェクトに格納されます。

個々のBEAM命令の詳細な一覧は本翻訳の対象外です。 原文オンライン版(https://blog.stenmans.org/theBeamBook/#AP-Instructions)を参照してください。

個別の命令

引数の型は次のとおりです。

説明
a即値のアトム。例: 'foo'
c即値の定数値(アトム、nil、small int)。Pid?かもしれない
dレジスタまたはスタックスロットのいずれか
eexportテーブルエントリへの参照
fラベル、すなわちコードアドレス
I整数。例: 42
j省略可能なコードラベル
l浮動小数点レジスタ
P正の(符号なし)整数リテラル
rレジスタR0(x[0]
sリテラル、レジスタ、スタックスロットのいずれか
t項。例: [{foo, bar}]
xレジスタ。例: {x, 5}に対する5
yスタックスロット。例: {y, 1}に対する1

すべてのBEAM命令の一覧

命令引数説明
allocatet tスタック上にいくつかのワードを確保する
allocate_heapt I tヒープ上にいくつかのワードを確保する
allocate_heap_zerot I tヒープを確保し、ワードをNILに設定する
allocate_initt I y
allocate_zerot tスタックを確保し、ワードを0に設定する?
applyIx[Arity]のモジュールとx[Arity+1]の関数に対して、x[0..Arity-1]の引数を適用する
apply_lastI Papplyと同様だが、CPを保存せずPワードを解放する
badargjbadargエラーを生成する
badmatchrxybadmatchエラーを生成する
bif1f b s d1引数でbifを呼び出し、失敗した場合はfへジャンプする
bif1_bodyb s d
bs_context_to_binaryrxy
bs_put_stringI I
bs_test_tail_imm2f rx I
bs_test_unitf rx I
bs_test_unit8f rx
bs_test_zero_tail2f rx
call_bif0e
call_bif1e
call_bif2e
call_bif3e
case_endrxycase_clauseエラーを生成する
catchy f
catch_endy
deallocateIスタックからいくつかのワードを解放し、CPをポップする
deallocate_returnQdeallocatereturnを組み合わせる
extract_next_elementxy
extract_next_element2xy
extract_next_element3xy
fclearerror
fconvd l
fmoveqdl ld
get_listrxy rxy rxyリストセルをheadとtailに分解する
i_apply引数x2で関数x0:x1のコードを呼び出し、CPを保存する
i_apply_fun引数x1で関数オブジェクトx0のコードを呼び出し、CPを保存する
i_apply_fun_lastP引数x1で関数オブジェクトx0のコードへジャンプし、CPを復元してP個のスタックセルを解放する
i_apply_fun_only引数x1で関数オブジェクトx0のコードへジャンプする
i_apply_lastP引数x2で関数x0:x1のコードへジャンプする
i_apply_only引数x2で関数x0:x1のコードへジャンプする
i_bandj I d
i_bif2f b d
i_bif2_bodyb d
i_borj I d
i_bs_addj I d
i_bs_appendj I I I d
i_bs_get_binary2f rx I s I d
i_bs_get_binary_all2f rx I I d
i_bs_get_binary_all_reuserx f I
i_bs_get_binary_imm2f rx I I I d
i_bs_get_float2f rx I s I d
i_bs_get_integerf I I d
i_bs_get_integer_16rx f d
i_bs_get_integer_32rx f I d
i_bs_get_integer_8rx f d
i_bs_get_integer_immrx I I f I d
i_bs_get_integer_small_immrx I f I d
i_bs_get_utf16rx f I d
i_bs_get_utf8rx f d
i_bs_initI I d
i_bs_init_bitsI I d
i_bs_init_bits_failrxy j I d
i_bs_init_bits_fail_heapI j I d
i_bs_init_bits_heapI I I d
i_bs_init_failrxy j I d
i_bs_init_fail_heapI j I d
i_bs_init_heapI I I d
i_bs_init_heap_binI I d
i_bs_init_heap_bin_heapI I I d
i_bs_init_writable
i_bs_match_stringrx f I I
i_bs_private_appendj I d
i_bs_put_utf16j I s
i_bs_put_utf8j s
i_bs_restore2rx I
i_bs_save2rx I
i_bs_skip_bits2f rx rxy I
i_bs_skip_bits2_imm2f rx I
i_bs_skip_bits_all2f rx I
i_bs_start_match2rxy f I I d
i_bs_utf16_sizes d
i_bs_utf8_sizes d
i_bs_validate_unicodej s
i_bs_validate_unicode_retractj
i_bslj I d
i_bsrj I d
i_bxorj I d
i_callf
i_call_exte
i_call_ext_laste P
i_call_ext_onlye
i_call_funI
i_call_fun_lastI P
i_call_lastf P
i_call_onlyf
i_elementrxy j s d
i_faddl l l
i_fast_elementrxy j I d
i_fcheckerror
i_fdivl l l
i_fetchs s
i_fmull l l
i_fnegatel l l
i_fsubl l l
i_func_infoI a a Ifunction_clauseエラーを生成する
i_gc_bif1j I s I d
i_gc_bif2j I I d
i_gc_bif3j I s I d
i_gets d
i_get_tuple_elementrxy P rxy
i_hibernate
i_incrementrxy I I d
i_int_bnotj s I d
i_int_divj I d
i_is_eqf
i_is_eq_exactf
i_is_eq_exact_immedf rxy c
i_is_eq_exact_literalf rxy c
i_is_gef
i_is_ltf
i_is_nef
i_is_ne_exactf
i_is_ne_exact_immedf rxy c
i_is_ne_exact_literalf rxy c
i_jump_on_valrxy f I I
i_jump_on_val_zerorxy f I
i_loop_recf r
i_m_divj I d
i_make_funI t
i_minusj I d
i_move_callc r f
i_move_call_extc r e
i_move_call_ext_laste P c r
i_move_call_ext_onlye c r
i_move_call_lastf P c r
i_move_call_onlyf c r
i_new_bs_put_binaryj s I s
i_new_bs_put_binary_allj s I
i_new_bs_put_binary_immj I s
i_new_bs_put_floatj s I s
i_new_bs_put_float_immj I I s
i_new_bs_put_integerj s I s
i_new_bs_put_integer_immj I I s
i_plusj I d
i_put_tuplerxy IアリティIのタプルを生成し、結果をrxyに格納する。要素は続くput命令で設定する
i_recv_setf
i_remj I d
i_select_tuple_arityr f I
i_select_tuple_arityx f I
i_select_tuple_arityy f I
i_select_tuple_arity2r f A f A f
i_select_tuple_arity2x f A f A f
i_select_tuple_arity2y f A f A f
i_select_valr f I値を{Value, Label}のペアのリストと比較し、一致すればジャンプする。一致しなければfへジャンプする
i_select_valx f I上と同様だがxレジスタの場合
i_select_valy f I上と同様だがyレジスタの場合
i_select_val2r f c f c f値を{c1, f1}または{c2, f2}の2つのペアと比較してジャンプする。失敗した場合はfへジャンプする
i_select_val2x f c f c f上と同様だがxレジスタの場合
i_select_val2y f c f c f上と同様だがyレジスタの場合
i_timesj I d
i_trimIスタックをI要素分切り詰め、先頭のCPは保持する
i_wait_error
i_wait_error_locked
i_wait_timeoutf I
i_wait_timeoutf s
i_wait_timeout_lockedf I
i_wait_timeout_lockedf s
if_endif_clauseエラーを生成する
inityスタック上の1ワードをNIL[])に設定する
init2y yスタック上の2ワードをNIL[])に設定する
init3y y yスタック上の3ワードをNIL[])に設定する
int_code_endプログラムの終端(スタックなしのreturnと同じ)
is_atomf rxy値がアトムかどうかを調べ、そうでなければジャンプする
is_bitstringf rxy値がビット列かどうかを調べ、そうでなければジャンプする
is_booleanf rxy値がアトムtrueまたはfalseかどうかを調べ、そうでなければジャンプする
is_floatf rxy値が浮動小数点数かどうかを調べ、そうでなければジャンプする
is_functionf rxy値が関数かどうかを調べ、そうでなければジャンプする
is_function2f s s値が関数かどうかを調べ、そうでなければジャンプする
is_integerf rxy値がbig integerまたはsmall integerかどうかを調べ、そうでなければジャンプする
is_integer_allocatef rx I I
is_listf rxy値がリストまたはNILかどうかを調べ、そうでなければジャンプする
is_nilf rxy値が空リスト[]かどうかを調べ、そうでなければジャンプする
is_nonempty_listf rxy値が空でないリスト(consポインタ)かどうかを調べ、そうでなければジャンプする
is_nonempty_list_allocatef rx I t
is_nonempty_list_test_heapf r I t
is_numberf rxy値がbig integer、small integer、floatのいずれかかどうかを調べ、そうでなければジャンプする
is_pidf rxy値がpidかどうかを調べ、そうでなければジャンプする
is_portf rxy値がportかどうかを調べ、そうでなければジャンプする
is_referencef rxy値がreferenceかどうかを調べ、そうでなければジャンプする
is_tuplef rxy値がタプルかどうかを調べ、そうでなければジャンプする
is_tuple_of_arityf rxy A値がアリティAのタプルかどうかを調べ、そうでなければジャンプする
jumpf位置(ラベル)fへジャンプする
labelLコード中の位置を示すマーカーで、ロード時に取り除かれる
lineIソースファイル中の位置を示すマーカーで、ロード時に取り除かれる
loop_rec_endfプロセスのreceiveポインタを進め、loop_rec命令へジャンプする
moverxync rxy値またはレジスタを別のレジスタへ移動する
move2x x x x値のペアを移動先のペアへ移動する
move2x y x y値のペアを移動先のペアへ移動する
move2y x y x値のペアを移動先のペアへ移動する
move_callxy r f
move_call_lastxy r f Q
move_call_onlyx r f
move_deallocate_returnxycn r Q
move_jumpf ncxy
move_returnxcn r
move_x1c値をx1に格納する
move_x2c値をx2に格納する
noderxyrxyを現在のノード名を表すアトムにする
putrxyこの命令の並びはi_put_tupleの後に置かれ、タプルの要素を(0番目から)初期化するために使われる
put_lists s dheadとtailからリストセルを構築し、consポインタを移動先dに格納する
raises s指定した型の例外を送出する。互換性維持の都合上、例外の型は第2引数のスタックトレースから取り出す必要がある
recv_markfメッセージ取得の再開位置として既知の位置に印を付ける(参照の最適化)
remove_messageプロセスのinboxから現在のメッセージを取り除く(受信済みとする)
returnCPにあるアドレスへジャンプし、CPを0に設定する
selfrxyrxyを現在のプロセスのpidに設定する
sendメッセージx1をプロセスx0のinboxへ送信する。プロセスが存在しなくてもエラーにはならない
set_tuple_elements d P添字を使ってタプルの要素を破壊的に更新する
system_limitj
test_arityf rxy Arxyにある関数オブジェクト(クロージャまたはexport)がアリティAかどうかを調べ、そうでなければfへジャンプする
test_heapI tヒープの空き容量を確認する
test_heap_1_put_listI y
timeoutタイマーを設定し、メッセージの到着かタイマーイベントのどちらか早い方を待ってプロセスの実行を明け渡す
timeout_locked
tryy fスタックセルyに特別なcatch値を書き込み、tryブロックが有効であることを示す。例外が発生するとVMはラベルfへジャンプする。これ以降実行されるコードは例外から保護される
try_caseytry_endと同様に保護区間の終わりを示し、スタック上のcatch値をクリアして例外マッチングのコード区間を開始する
try_case_ends
try_endyスタックセルyからcatch値をクリアし、保護区間の終わりを示す
waitfメッセージの到着を待ってプロセスをスケジュールアウトする(明け渡す)
wait_lockedf
wait_unlockedf