Major changes: - LLVM backend initial implementation (compiler.rs, llvm mode) - Semantics layer integration in interpreter (operators.rs) - Phase 12 plugin architecture revision (3-layer system) - Builtin box removal preparation - MIR instruction set documentation (26→Core-15 migration) - Cross-backend testing infrastructure - Await/nowait syntax support New features: - LLVM AOT compilation support (--backend llvm) - Semantics layer for interpreter→VM flow - Tri-backend smoke tests - Plugin-only registry mode Bug fixes: - Interpreter plugin box arithmetic operations - Branch test returns incorrect values Documentation: - Phase 12 README.md updated with new plugin architecture - Removed obsolete NYIR proposals - Added LLVM test programs documentation Co-Authored-By: Claude <noreply@anthropic.com>
4.8 KiB
4.8 KiB
埋め込みVMでのBox処理設計
🎯 核心:MIRレベルでのBox処理を再現
現在のMIR/VMでのBox処理フロー
1. MIR: BoxCall/PluginInvoke命令
↓
2. VM: ValueId → VMValue変換
↓
3. VMValue → Box<dyn NyashBox> or TLVエンコード
↓
4. メソッド実行
↓
5. 結果をVMValueに戻す
📊 埋め込みVMの設計
1. 軽量VMValue定義
// 埋め込みVM用の値表現
typedef enum {
NYVM_TYPE_INT,
NYVM_TYPE_FLOAT,
NYVM_TYPE_BOOL,
NYVM_TYPE_STRING,
NYVM_TYPE_HANDLE, // Box参照(ハンドル)
NYVM_TYPE_VOID
} NyVMType;
typedef struct {
NyVMType type;
union {
int64_t i;
double f;
uint8_t b;
struct { const char* data; size_t len; } s;
uint64_t h; // ハンドル(Box参照)
} value;
} NyVMValue;
2. MIRバイトコード形式
// BoxCall命令のエンコード
enum {
OP_BOXCALL = 0x20,
OP_PLUGIN_INVOKE = 0x21,
// ...
};
// BoxCall: [op:1] [dst:1] [box_val:1] [method_id:2] [argc:1] [args...]
// 例: BoxCall %2 = %1.toString()
// → 0x20 0x02 0x01 0x00 0x00 0x00
3. 埋め込みVMでのBoxCall実行
int nyvm_execute_boxcall(
NyashEmbeddedVM* vm,
uint8_t dst,
uint8_t box_val,
uint16_t method_id,
uint8_t argc,
uint8_t* args
) {
// 1. レシーバー取得
NyVMValue* recv = &vm->values[box_val];
// 2. プリミティブ型の場合
if (recv->type != NYVM_TYPE_HANDLE) {
// プリミティブ→TLV変換
uint8_t tlv_buf[256];
size_t tlv_len = encode_primitive_to_tlv(recv, tlv_buf);
// 組み込み実装を呼び出し
return call_builtin_method(recv->type, method_id, tlv_buf, tlv_len);
}
// 3. Box(ハンドル)の場合
uint64_t handle = recv->value.h;
// 引数をTLVエンコード
uint8_t args_tlv[1024];
size_t args_len = 0;
for (int i = 0; i < argc; i++) {
NyVMValue* arg = &vm->values[args[i]];
args_len += encode_value_to_tlv(arg, &args_tlv[args_len]);
}
// 4. プラグイン呼び出し(C ABI)
uint8_t result[4096];
size_t result_len = sizeof(result);
int rc = nyash_plugin_invoke(
get_type_id_from_handle(handle),
method_id,
get_instance_id_from_handle(handle),
args_tlv, args_len,
result, &result_len
);
// 5. 結果をVMValueに変換
if (rc == 0) {
decode_tlv_to_value(result, result_len, &vm->values[dst]);
}
return rc;
}
🔄 Box引数の処理
現在のVM(Rust)
// VMValue → Box<dyn NyashBox>変換
let val = self.get_value(*arg)?;
Ok(val.to_nyash_box())
埋め込みVM(C)
// NyVMValue → TLVエンコード
switch (value->type) {
case NYVM_TYPE_INT:
encode_i64(tlv, value->value.i);
break;
case NYVM_TYPE_HANDLE:
encode_handle(tlv,
get_type_id_from_handle(value->value.h),
get_instance_id_from_handle(value->value.h)
);
break;
// ...
}
💡 実装のポイント
1. ハンドル管理
// グローバルハンドルテーブル
typedef struct {
uint32_t type_id;
uint32_t instance_id;
void* native_ptr; // 実際のBoxポインタ(必要な場合)
} HandleEntry;
static HandleEntry g_handles[MAX_HANDLES];
static uint64_t g_next_handle = 1;
uint64_t register_handle(uint32_t type_id, uint32_t instance_id) {
uint64_t h = g_next_handle++;
g_handles[h].type_id = type_id;
g_handles[h].instance_id = instance_id;
return h;
}
2. 組み込みメソッド
// 頻出メソッドは埋め込みVMに直接実装
int call_builtin_method(NyVMType type, uint16_t method_id, ...) {
switch (type) {
case NYVM_TYPE_INT:
if (method_id == 0) { // toString
// 整数→文字列変換
}
break;
// ...
}
}
3. プラグインとの統合
// 生成されるCコード
extern "C" int32_t nyplug_mybox_invoke(...) {
// MIRバイトコード実行
NyashEmbeddedVM vm;
nyvm_init(&vm, BYTECODE, sizeof(BYTECODE));
// 引数をVMスタックに設定
nyvm_decode_args(&vm, args, args_len);
// メソッド実行
nyvm_execute_method(&vm, method_id);
// 結果をTLVエンコード
return nyvm_encode_result(&vm, result, result_len);
}
🎯 結論
埋め込みVMは:
- MIRのBoxCall/PluginInvoke命令を忠実に実装
- TLVエンコード/デコードでC ABIと通信
- ハンドルでBox参照を管理
- 頻出処理は最適化実装
これにより、Nyashスクリプトで書いたプラグインも、ネイティブプラグインと同じC ABIで動作します!