命令セット
This chapter is the bytecode reference. It lists every instruction the Keleusma virtual machine executes, so the disassembly shown in the playground bytecode view has a place to be looked up. It reproduces the authoritative docs/spec/INSTRUCTION_SET.md from the repository.
Keleusma 仮想マシンは、ブロック構造の制御フローを用いてスタックベースのバイトコードを実行します。すべての命令は値スタック上で動作します。本文書は、すべての命令を、そのオペランド、動作、および WCET(最悪実行時間)と WCMU(最悪メモリ使用量)の解析へのコスト寄与とともに一覧します。
各命令は相対的な整数コストを持ちます。コストは単位のない相対的な重みであり、サイクル数ではありません。値が大きいほど高価な演算を示します。コスト表は wcet_stream_iteration() によって参照され、命令ごとのスタックおよびヒープへの影響は wcmu_stream_iteration() によって参照されます。
バイトコードがソースからどのように生成されるかの詳細については、第25章「ソースからバイトコードへ」を参照してください。フレーミングヘッダ、オペコードストリームのエンコード、およびオペランドプールを含むバイトコードのワイヤフォーマットについては、EXECUTION_MODEL.md を参照してください。ブロック階層および検証規則を含む構造的 ISA 仕様については、STRUCTURAL_ISA.md を参照してください。
定数
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| Const | u16 index | 1 | チャンクの定数プールから定数をプッシュします。 |
| PushImmediate | u8 value | 1 | インラインの即値をプッシュします。オペランドは、少数の番兵値または小さな整数のいずれかをエンコードします。後述の「PushImmediate encoding」を参照してください。 |
PushImmediate エンコード
| オペランド値 | 意味 |
|---|---|
0 | Value::Unit |
1 | Value::Bool(true) |
2 | Value::Bool(false) |
3 | Value::None(Option::None の番兵) |
4 | Value::Int(0) |
5 | Value::Int(1) |
| … | … |
19 | Value::Int(15) |
20..255 | 予約済み。デコーダは破損の信号として扱います。 |
十六個の小さな整数リテラル(Int(0) から Int(15))がインラインでエンコードされます。より大きなリテラルや即値でないリテラルは、引き続き定数プールを参照する Const を使用します。抽出規則は予測可能です。オペランド値 0..3 は番兵を選択し、値 4..19 は Int(value - 4) を選択し、値 20..255 は破損を示します。
ローカル変数
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| GetLocal | u16 slot | 1 | ローカル変数をスタックにプッシュします。 |
| SetLocal | u16 slot | 1 | スタックをポップしてローカル変数スロットに格納します。 |
データセグメント
統一されたスロットインデックス空間は、共有スロット [0, shared_count) とプライベートスロット [shared_count, shared_count + private_count) に分割されます。共有スロットは各呼び出し時に借用されるホスト所有のバッファに存在し、ホストは Vm::get_shared/Vm::set_shared(B28 項目 2)を通じて到達します。プライベートスロットはスクリプト専用であり、アリーナの永続領域に存在します。以下のオペコードは両方の区画を許容します。ランタイムはスロットインデックスをキャッシュされた shared_slot_count と比較して振り分け、共有スロットはバイトオフセットで借用バッファに、プライベートスロットはアリーナに送ります。const データフィールドはスロットを消費しません。フィールド読み出しは Const にコンパイルされ、書き込みはコンパイルエラーとなります。
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| GetData | u16 slot | 1 | データセグメントのスロット値をスタックにプッシュします。 |
| SetData | u16 slot | 1 | 値をポップしてデータセグメントのスロットに格納します。スカラーはインラインで格納されます。フラットな複合体は、モジュールのプライベート複合体レイアウトテーブルがそのスロットに対して記録するオフセットで、その本体を永続複合体プールにコピーします(専用の複合体書き込みオペコードはありません)。 |
| GetDataIndexed | u16 base, u16 len | 2 | 配列インデックスをポップし、len に対して境界チェックを行い、base + index の値をプッシュします。 |
| SetDataIndexed | u16 base, u16 len | 2 | 配列インデックスをポップし、次に値をポップし、len に対して境界チェックを行い、base + index のスロットに格納します。 |
| BoundsCheck | u16 bound | 2 | スタックの先頭を Int として覗き見し、[0, bound) の範囲外であればトラップします。多次元インデックスアクセスのレベル間でコンパイラによって発行されます。 |
算術
整数算術はチェック付き算術ファミリを使用します。CheckedAdd、CheckedSub、CheckedMul、CheckedNeg の各オペコードは Value::Int オペランドをポップし(二項形式では二つ、CheckedNeg では一つ)、真の結果を i128 で計算し、三つのスロット、すなわち下位半分、上位半分、および結果フラグ(Int(0) は正常、Int(1) はオーバーフロー、Int(2) はアンダーフロー)をプッシュします。プッシュ順序は low を最下部に、flag を最上部に配置するため、Int オペランドに対する a + b のような表層のラッピング式は、チェック付きオペコードに続けて PopN(2) にコンパイルされ、ラッピング結果をスタックに残します。ソースレベルのパターンアームによるマッチングは、この三つの出力を分解します。
ラッピング算術オペコード Add、Sub、Mul、Neg は命令セットに残っていますが、もはや Value::Int オペランドを受け付けません。許可されるオペランド型は Byte、Fixed、Float です。V0.2.0 の Consolidation B パスは、すべての Int 算術をチェック付きファミリに経由させることでこれらのオペコードを狭めました。コンパイラはすべての Int オペランド位置に対して CheckedXxx; PopN(2) を発行します。コンパイラが型を静的に推論できないオペランドも同様に Int パスに落ちます。なぜなら Word がデフォルトの数値型だからです。
Op::Div および Op::Mod は Int、Byte、Float に対して多相のままです。そのチェック付き対応物である CheckedDiv および CheckedMod は、符号付き除算のコーナーケースを露出します。
CheckedMul および CheckedDiv は、整数または Q 形式の算術を選択する u8 の小数ビット数を持ちます。ここで 0 は整数であり、正の数は Fixed です。これは整数データパスと固定小数点データパスが異なる唯一の箇所であり、すなわち乗算または除算の前後での小数ビット数によるシフトです。そのため、別々のオペコードではなく単一のパラメータ化されたオペコードが両方を担います。CheckedAdd、CheckedSub、CheckedMod、CheckedNeg は、その Fixed 形式がシフトを伴わず、オペランド型のみで振り分けられるため、このようなパラメータを必要としません。
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| CheckedAdd | なし | 2 | 二つの Int オペランドをポップし、(low, high, flag) をプッシュします。flag および high の半分は、共有の vm::checked_arith_outputs ヘルパーを通じて、バイトコードで宣言されたワード幅で報告されます。 |
| CheckedSub | なし | 2 | 二つの Int オペランドをポップし、(low, high, flag) をプッシュします。 |
| CheckedMul | u8 frac_bits | 2 | 二つのオペランドをポップし、(low, high, flag) をプッシュします。小数ビット数が形式を選択します。0 の場合は整数乗算であり、上位半分が大きな数の乗算における載荷値となります。0 より大きい数の場合、オペランドは Fixed であり、i128 の積は範囲チェックの前にそのビット数だけ右シフトされ、ラップされた結果は単一のワードであり、上位スロットは未使用です。したがって小数ビットが 0 の場合はちょうど整数乗算です。 |
| CheckedNeg | なし | 2 | 一つの Int オペランドをポップし、(low, high, flag) をプッシュします。デフォルトの 64 ビット宣言幅における唯一のオーバーフローケースは -i64::MIN です。 |
| CheckedDiv | u8 frac_bits | 2 | 二つのオペランドをポップし、(low, high, flag) をプッシュします。小数ビット数が形式を選択します。0 の場合は整数除算であり、デフォルトの 64 ビット幅における唯一のオーバーフローケースは i64::MIN / -1 です。0 より大きい数の場合、オペランドは Fixed であり、被除数は除算の前に i128 の領域でそのビット数だけ左シフトされます。ゼロ除数はトラップするのではなく、分子を運ぶフラグ 3 として具現化されます。したがって小数ビットが 0 の場合はちょうど整数除算です。 |
| CheckedMod | なし | 2 | 二つの Int オペランドをポップし、(low, high, flag) をプッシュします。ゼロ除算でトラップします。デフォルトの 64 ビット宣言幅における唯一のオーバーフローケースは i64::MIN % -1 です。 |
| Add | なし | 2 | Byte、Fixed、Float 型の二つのオペランドをポップし、ラッピングまたは IEEE 754 の和をプッシュします。Int オペランド位置は除外されます。コンパイラは Int + Int を CheckedAdd; PopN(2) に経由させます。 |
| Sub | なし | 2 | Byte、Fixed、Float 型の二つのオペランドをポップし、ラッピングまたは IEEE 754 の差をプッシュします。Int オペランド位置は除外されます。 |
| Mul | なし | 2 | Byte または Float 型の二つのオペランドをポップし、ラッピングまたは IEEE 754 の積をプッシュします。Fixed の乗算は FixedMul(n) を使用します。Int オペランド位置は除外されます。 |
| Neg | なし | 2 | Byte、Fixed、Float 型の一つのオペランドをポップし、ラッピングまたは IEEE 754 の否定をプッシュします。Int オペランド位置は除外されます。 |
| Div | なし | 3 | 二つの値をポップし、商をプッシュします。ゼロ除算でトラップします。オーバーフローフラグはありません。 |
| Mod | なし | 3 | 二つの値をポップし、剰余をプッシュします。ゼロ除算でトラップします。オーバーフローフラグはありません。 |
比較
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| CmpEq | なし | 2 | 二つの値をポップし、等しければ true をプッシュします。 |
| CmpNe | なし | 2 | 二つの値をポップし、等しくなければ true をプッシュします。 |
| CmpLt | なし | 2 | 二つの値をポップし、より小さければ true をプッシュします。 |
| CmpGt | なし | 2 | 二つの値をポップし、より大きければ true をプッシュします。 |
| CmpLe | なし | 2 | 二つの値をポップし、以下であれば true をプッシュします。 |
| CmpGe | なし | 2 | 2つの値をポップし、以上であれば true をプッシュします。 |
論理
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| Not | なし | 1 | boolean をポップし、論理否定をプッシュします。 |
短絡評価の AND と OR はバイトコードレベルでは If 分岐としてエンコードされます。LogicalAnd や LogicalOr のオペコードは存在しません。
ビット演算
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| BitAnd | なし | 2 | 2つの Int オペランドをポップし、ビット単位の AND をプッシュします。 |
| BitOr | なし | 2 | 2つの Int オペランドをポップし、ビット単位の OR をプッシュします。 |
| BitXor | なし | 2 | 2つの Int オペランドをポップし、ビット単位の XOR をプッシュします。 |
| Shl | なし | 2 | シフト数を、続いて値をポップします。値を count & (word_width - 1) だけ左シフトしてプッシュします。 |
| Shr | なし | 2 | シフト数を、続いて値をポップします。算術右シフトした値(符号を保持)をプッシュします。 |
制御フロー
ブロック構造の制御フローオペコードは u16 のジャンプ先を保持します。したがってチャンクのオペコード数は 65,535(CHUNK_SIZE_HARD_LIMIT)に上限が定められています。コンパイラは単一のチャンクが上限の 80%(52,428 命令、CHUNK_SIZE_SOFT_WARN_THRESHOLD)を超えたときに CompileWarning を発行し、ヘルパー関数への分解を促します。上限におけるバイトコードは有効なままです。CHUNK_SIZE_HARD_LIMIT を超えるチャンクはコンパイル時に CompileError として拒否されます。ホストは keleusma::compiler::compile_with_warnings(program, target) を呼び出すことで、モジュールとともに警告ベクトルを受け取ります。compile_with_target と compile は、警告を必要としない呼び出し元のために警告を破棄します。
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| If | u16 offset | 1 | boolean をポップします。false であれば、対応する Else または EndIf まで前方にスキップします。 |
| Else | u16 offset | 1 | 対応する EndIf まで無条件に前方へスキップします。 |
| EndIf | なし | 1 | if または if-else ブロックの終端です。No-op です。 |
| Loop | u16 offset | 1 | loop ブロックの開始です。オフセットは対応する EndLoop までの距離です。 |
| EndLoop | u16 offset | 1 | 対応する Loop へ無条件に後方ジャンプします。 |
| Break | u16 depth | 1 | 指定されたネスト深度にある外側の loop を抜けます。 |
| BreakIf | u16 depth | 1 | boolean をポップします。true であれば、指定されたネスト深度にある外側の loop を抜けます。 |
関数呼び出し
ネイティブ関数呼び出しは、ソースレベルの use 宣言と、それに対応するホスト側の登録 ABI によって区別される2つのクラスに分かれます。
- 検証済みネイティブ。
use module::nameでインポートされます。ホストはVm::register_verified_native(name, fn, wcet_bound, wcmu_bound)を通じて登録します。ホストが証明したコストは、イテレーションの WCET および WCMU の予算に組み込まれます。コンパイラはCallVerifiedNativeを発行します。 - 外部ネイティブ。
use external module::nameでインポートされます。ホストはVm::register_external_native(name, fn, max_invocations_per_iteration)を通じて登録します。ホストは呼び出しごとのコストではなく、イテレーションごとの呼び出し回数の上限を証明します。コンパイラはCallExternalNativeを発行します。
ランタイムは Vm::call_function の入口と、Vm::verify_native_classifications の明示的な呼び出しのたびに、宣言された各ネイティブをそのホスト登録に対して相互検証します。この検証はモジュール内のすべてのネイティブ呼び出し箇所を走査し、不一致(例えば use math::sqrt をインポートしているバイトコードに対し、ホストが register_external_native を通じて sqrt を登録している場合)を VmError::VerifyError として拒否します。結果は最初の走査に成功した後にキャッシュされます。いかなる register_* 呼び出しや replace_module 呼び出しもキャッシュを無効化します。不一致を最初の呼び出し時ではなくデプロイ検証の段階で明らかにしたいホストは、登録後に verify_native_classifications を明示的に呼び出すことができます。
V0.2.0 のフェーズ5で、検証済みと外部の分割が導入されました。従来の Op::CallNative オペコードは廃止されました。すべてのネイティブ呼び出し箇所は Op::CallVerifiedNative または Op::CallExternalNative のいずれかにコンパイルされます。以前 Vm::register_native を呼び出していたホストは、このメソッドが検証済み分類を付与するため、引き続き検証済みネイティブを登録します。
外部ネイティブに対する max_invocations_per_iteration の証明は登録時に記録されますが、まだ WCMU の境界には組み込まれていません。検証器は外部ネイティブがスクリプトのイテレーションごとの WCMU 予算にゼロを寄与するものとして扱います。ホストは外部ネイティブがスクリプトのリソース契約の外にあることを受け入れ、それらのリソース使用を別途検証しています。チャンクレベルの統合(外部ネイティブのコストを静的な呼び出し箇所ごとではなく、チャンクごとに max_invocations_per_iteration * per_call_wcmu として境界づけるもの)は、B20 の下で追跡される将来の作業です。
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| Call | u16 chunk_idx, u8 argc | 10 | インデックスによってコンパイル済みチャンクを argc 個の引数で直接呼び出します。 |
| CallVerifiedNative | u16 native_idx, u8 argc | 10 | 検証済みネイティブ関数を呼び出します。コストはホストの証明に従ってイテレーション予算に組み込まれます。 |
| CallExternalNative | u16 native_idx, u8 argc | 10 | 外部ネイティブ関数を呼び出します。イテレーションのコスト予算は呼び出しの間は一時停止します。検証器はイテレーションごとの呼び出し回数を追跡します。 |
| Return | なし | 2 | 現在のチャンクから戻ります。 |
クロージャ構築および間接ディスパッチのオペコード(PushFunc、MakeClosure、MakeRecursiveClosure、CallIndirect)は ISA に存在しません。クロージャ形状の表層式は、その構文を名指しする診断とともに型検査の段階で拒否されます。第一級関数値も同様に拒否されます。Value::Func ランタイムバリアントは V0.2.0 のフェーズ4でオペコードとともに廃止されました。以前クロージャを使用していた表層プログラムは、トップレベルの fn 定義またはトレイトメソッドとして書き直す必要があります。
コルーチンとストリーミング
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| Yield | なし | 1 | 出力値をポップして中断します。再開時には、ホストの入力値がスタックにプッシュされます。 |
| Stream | なし | 1 | ストリーミング領域の入口です。Reset のみがこれを対象にできます。 |
| Reset | なし | 1 | アリーナのトップ領域をクリアし、スケジュールされていればホットスワップを有効化し、対応する Stream へジャンプします。 |
スタック
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| PopN | u8 count | 1 | スタックの先頭から count 個の値を破棄します。コンパイラは単一スロットのポップに対して PopN(1) を、複数スロットの破棄に対して PopN(n) を発行します。これには、ラッピングセマンティクスが望まれる場合のチェック付き算術オペコード後の3スロット破棄も含まれます。 |
| Dup | なし | 1 | スタックの先頭を複製します。 |
型構築
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| NewComposite | kind, count, byte_size または meta | 5 | count 個の値をポップし、指定された種類(struct、tuple、array、または enum)の複合体を1つ構築します。フラット形式はポップした値を byte_size バイトにパックします。ボックス形式はテンプレートインデックス meta からヒープ複合体を構築します。enum の先頭の判別子は count 個の値のうちの1つとして数えられます。この単一オペコードは、V0.2.0 の4つの構築オペコード(wire id 34〜37、廃止済み)を置き換えます。 |
tuple は無名の struct、array は同種の struct、フラットな enum は最初にパックされる値が判別子である struct であるため、フラット構築は4つの種類すべてにわたって1つの操作となります。オペランドは複合体の種類、オペランドスタックのポップ数、およびバイト単位の正確なフラット割り当てサイズ(フラット形式)または struct テンプレートインデックス(ボックス形式)のいずれかを保持します。フラットバイトサイズは、検証器が合計する正確な最悪メモリ使用量の寄与分です。下記のヒープ割り当ての表を参照してください。
Option::None センチネルと Option::Some ラップは、それぞれ PushImmediate(3) とラップされた値の自然な表現を通じて処理されます。専用の PushNone や WrapSome のオペコードは存在しません。
フィールドアクセス
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| GetField | StructField(フラット/ネスト/ボックス) | 3 | struct をポップし、フィールド値をプッシュします。ベイクされた StructField オペランドは、バイトオフセットでのフラット読み取り、ネスト複合体の抽出、またはボックス化された位置指定/名前指定の検索を選択します。 |
| GetIndex | ArrayElem(フラット/ネスト/ボックス) | 2 | インデックスと array をポップし、要素をプッシュします。ベイクされた ArrayElem オペランドは、index * element_size でのフラット読み取り、ネスト複合体の抽出、またはボックス化されたインデックスを選択します。 |
| GetTupleField | TupleField(フラット/ネスト/ボックス) | 2 | tuple をポップし、要素をプッシュします。ベイクされた TupleField オペランドは、バイトオフセットでのフラット読み取り、ネスト複合体の抽出、またはボックス化された位置指定インデックスを選択します。 |
| GetEnumField | EnumField(フラット/ネスト/ボックス) | 2 | enum バリアントをポップし、ペイロードフィールドをプッシュします。ベイクされた EnumField オペランドは、判別子ワードを越えたフラット読み取り、ネスト複合体の抽出、またはボックス化された位置指定インデックスを選択します。 |
| Len | なし | 2 | 複合体値(Array、Text、Tuple)をポップし、長さを Int としてプッシュします。 |
型テスト
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| IsEnum | u16 enum-name, u16 variant-name, u16 discriminant-value(すべて定数インデックス) | 3 | スタックの先頭を覗き見します。enum 型とバリアントに一致すれば true をプッシュします。 |
| IsStruct | u16 name | 3 | スタックの先頭を覗き見します。struct 型に一致すれば true をプッシュします。 |
キャストと固定小数点演算
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| IntToFloat | なし | 2 | Word をポップし、Float としてプッシュします。floats 機能でゲートされます。 |
| FloatToInt | なし | 2 | Float をポップし、Word としてプッシュします(ゼロ方向へ切り捨て)。floats 機能でゲートされます。 |
| WordToByte | なし | 2 | Word をポップし、下位8ビットを Byte としてプッシュします。 |
| ByteToWord | なし | 2 | Byte をポップし、Word にゼロ拡張します。 |
| WordToFixed | u8 frac_bits | 2 | Word をポップし、指定された小数ビット数を持つ対応する Q フォーマットの Fixed 値をプッシュします。 |
| FixedToWord | u8 frac_bits | 2 | Fixed をポップし、整数部分を Word としてプッシュします。飽和します。 |
| FixedMul | u8 frac_bits | 2 | 2つの Q フォーマットの Fixed 値をポップし、その積をプッシュします。i128 の積を小数ビット数だけ右シフトし、飽和します。 |
| FixedDiv | u8 frac_bits | 2 | 2つの Q フォーマットの Fixed 値をポップし、その商をプッシュします。除算の前に被除数を小数ビット数だけ左シフトし、飽和します。 |
フォールト
| 命令 | オペランド | コスト | 説明 |
|---|---|---|---|
| Trap | u16 message index | 1 | 定数プールからのエラーメッセージとともに実行を停止します。 |
オペコード数とオペランド形状の一覧
この命令セットには66個のオペコードが含まれます。B28 の統合により、V0.2.0 の4つの構築オペコード(NewStruct、NewEnum、NewArray、NewTuple、wire id 34〜37)は廃止され、単一の NewComposite オペコード(wire id 69)に置き換えられました。廃止された id は予約され、再利用されません。有効な最大 wire id は69です。オペランド形状:
以下の形状は src/bytecode.rs で定義されている Op バリアントのオペランド型であり、WIRE_FORMAT.md に文書化されているフラット化されたインラインのワイヤエンコーディングとは異なります。特に、4つのベイクされたフィールドおよび要素アクセスオペコードは、単純な整数ではなく専用のオペランド構造体を保持します。
| 形状 | 使用箇所 |
|---|---|
| なし(ゼロオペランド) | 33 個のオペコード(算術、比較、ビット演算、型強制、スタック操作、ストリーミング、コルーチンなど) |
u8 | 8 個のオペコード(PushImmediate、PopN、WordToFixed、FixedToWord、FixedMul、FixedDiv、CheckedMul、CheckedDiv) |
u16 | 14 個のオペコード(Const、GetLocal、SetLocal、GetData、SetData、IsStruct、If、Else、Loop、EndLoop、Break、BreakIf、BoundsCheck、Trap) |
(u16, u8) | 3 個のオペコード(Call、CallVerifiedNative、CallExternalNative) |
(u16, u16) | 2 個のオペコード(GetDataIndexed、SetDataIndexed) |
(u16, u16, u16) | 1 個のオペコード(IsEnum。列挙型名、バリアント名、および判別子値の定数インデックスを伴います) |
| ベイクされたアクセスオペランド(専用) | 4 個のオペコード(GetField(StructField)、GetIndex(ArrayElem)、GetTupleField(TupleField)、GetEnumField(EnumField))。それぞれがコンパイラによって選択された Flat、FlatNested、または Boxed 形式を持つ専用の B28 オペランド構造体を伴います。フラット形式はバイトオフセットとスカラーの kind(あるいはネストされた複合体の size と variant)をベイクします。ボックス化形式は B28 以前の Vec ボディ用に位置インデックスまたはフィールド名定数インデックスを保持します。src/bytecode.rs の StructField、ArrayElem、TupleField、および EnumField 列挙型を参照してください。 |
| NewComposite(専用) | 1 個のオペコード(NewComposite)。フラット形式は複合体の種別、オペランドスタックのポップ数(0 から 62)、および正確なフラットバイトサイズをレコードの 3 つのオペランドバイトにパックします。ボックス化形式、またはフラットフィールド数が 62 を超える場合は、24 ビットのオペランドプールインデックスを (count, byte_size-or-meta, boxed_flag) を保持する (u16, u16, u8) エントリにスピルします。 |
ワイヤーエンコーディングはこれらの Op レベルの形状をフラット化します。66 個のオペコードのうち 62 個は、常に 4 バイトのオペコードレコード内にオペランドをインラインで保持します。3 個のオペコード(GetDataIndexed、SetDataIndexed、および IsEnum)は、常にインデックスによってオペランドプール内のエントリを参照します。NewComposite は共通の小さなフラット形式ではオペランドをインラインで保持し、ボックス化形式またはフラットフィールド数が 62 を超える場合にのみ (u16, u16, u8) オペランドプールエントリを参照します。(u16, u8) オペコード(Call、CallVerifiedNative、CallExternalNative)は、u8 がレコードのバイト 3 に収まるためインラインに収まります。4 個のベイクされたアクセスオペコードは、コンパイラによって選択されたオペランド形式をインラインに収めます。これらの形状をエンコードするワイヤーフォーマットについては、EXECUTION_MODEL.md および WIRE_FORMAT.md を参照してください。
コストの概要
コストのグルーピングは bytecode::nominal_op_cycles を再現します。実時間の WCET を必要とするホストは、ターゲットに合わせてキャリブレーションされたカスタムの CostModel を提供します。
| コスト | 命令 |
|---|---|
| 1 | Const, PushImmediate, GetLocal, SetLocal, GetData, SetData, Dup, Not, If, Else, EndIf, Loop, EndLoop, Break, BreakIf, Stream, Reset, Yield, Trap, PopN |
| 2 | Add, Sub, Mul, Neg, CheckedAdd, CheckedSub, CheckedMul, CheckedNeg, CheckedDiv, CheckedMod, CmpEq, CmpNe, CmpLt, CmpGt, CmpLe, CmpGe, GetIndex, GetTupleField, GetEnumField, Len, IntToFloat, FloatToInt, WordToByte, ByteToWord, WordToFixed, FixedToWord, FixedMul, FixedDiv, Return, GetDataIndexed, SetDataIndexed, BoundsCheck, BitAnd, BitOr, BitXor, Shl, Shr |
| 3 | Div, Mod, GetField, IsEnum, IsStruct |
| 5 | NewComposite |
| 10 | Call, CallVerifiedNative, CallExternalNative |
WCMU への寄与
WCMU コストは、スタックスロットの増加、スタックスロットの縮小、およびバイト単位のヒープ割り当てとして別々に報告されます。定数 VALUE_SLOT_SIZE_BYTES はスロット数をバイトに変換します。パラメトリックな Vm<W, A, F> 形状は size_of::<GenericValue<W, F>>() を直接使用します。src/verify.rs の wcmu_stream_iteration() によって計算されます。
スタックの増加(実行中のピーク正味デルタ)
これらの値は src/bytecode.rs の Op::stack_growth を再現します。複数出力のオペコードについては、値は生のプッシュ数ではなく、開始深度に対する正味のピークデルタです。例えば CheckedAdd は 2 つをポップして 3 つをプッシュするため、開始に対する相対的なピーク深度は +1 です。IsEnum と IsStruct は被検査値をピーク(ポップなし)して Bool をプッシュするため、正味デルタは +1 です。
| 増加 | 命令 |
|---|---|
| 0 | Not, Neg, Add, Sub, Mul, Div, Mod, CmpEq, CmpNe, CmpLt, CmpGt, CmpLe, CmpGe, SetLocal, SetData, SetDataIndexed, BoundsCheck, If, BreakIf, Else, EndIf, Loop, EndLoop, Break, Stream, Reset, Yield, Return, GetField, GetIndex, GetTupleField, GetEnumField, Len, IntToFloat, FloatToInt, WordToByte, ByteToWord, WordToFixed, FixedToWord, FixedMul, FixedDiv, Trap, PopN, BitAnd, BitOr, BitXor, Shl, Shr |
| 1 | Const, PushImmediate, GetLocal, GetData, Dup, GetDataIndexed, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod, Call, CallVerifiedNative, CallExternalNative, NewComposite, IsEnum, IsStruct |
| 2 | CheckedNeg |
スタックの縮小(実行中にポップされたスロット)
これらの値は Op::stack_shrink を再現します。正味デルタが非負であるオペコード(例えば CheckedAdd、CheckedNeg)については、検証器が stack_growth を通じてピークを計上し、正味のポップが存在しないため、縮小はゼロになります。
| 縮小 | 命令 |
|---|---|
| 0 | Const, PushImmediate, GetLocal, GetData, Dup, Not, Neg, CheckedAdd, CheckedSub, CheckedMul, CheckedNeg, CheckedDiv, CheckedMod, BoundsCheck, Else, EndIf, Loop, EndLoop, Break, Stream, Reset, Return, Len, IsEnum, IsStruct, IntToFloat, FloatToInt, WordToByte, ByteToWord, WordToFixed, FixedToWord, FixedMul, FixedDiv, Trap |
| 1 | Add, Sub, Mul, Div, Mod, CmpEq, CmpNe, CmpLt, CmpGt, CmpLe, CmpGe, SetLocal, SetData, GetDataIndexed, If, BreakIf, Yield, GetField, GetIndex, GetTupleField, GetEnumField, BitAnd, BitOr, BitXor, Shl, Shr |
| 2 | SetDataIndexed |
| n | PopN(n), Call(, n), CallVerifiedNative(, n), CallExternalNative(_, n), NewComposite(count) |
ヒープ割り当て(バイト)
| ヒープ | 命令 |
|---|---|
| 0 | 以下に列挙されていないすべての命令 |
オペランドからの byte_size | NewComposite、フラット形式。正確なフラット割り当てサイズはコンパイル時にオペランドにベイクされるため、最悪メモリ使用量への寄与は count * VALUE_SLOT_SIZE_BYTES の推定値ではなく正確なバイト数になります。ボックス化形式はフラットバイトをゼロと報告します。そのボディはヒープの Vec であり、別途計上されます。 |
| ホストによる証明 | CallVerifiedNative はホスト登録を通じて、CallExternalNative は反復ごとの呼び出し回数の上限を通じて |