機能

このページは Lambda C が提供する機能と API のカタログです。VM の内部構造、4バイト命令フォーマット、FFI のリンク機構、3スタックモデル、ホットリロード、設計判断などについては アーキテクチャ を参照してください。

言語機能

サポート

サポート(プリプロセッサ)

非サポート

FFIシステム

関数登録

// 基本登録(-1000から始まる負のIDを返す)
int lcvm_register_function(const char *name, lcvm_ffi_func_t func, int arg_count);

// 型シグネチャ付き登録(DEBUGビルドで型チェック)
int lcvm_register_function_ex(const char *name, lcvm_ffi_func_t func,
                               int arg_count, const char *signature);

// 名前で関数を検索(手動リンク用)
int lcvm_ffi_find_by_name(const char *name, int *arg_count);

// 自動リンク(バイトコードロード後に呼び出し)
int lcvm_link_program(compact_program_t *prog);

型シグネチャ

lcvm_register_function_ex() で使用するシグネチャ文字:

文字
iint
ddouble
ffloat
ppointer
sstring
// 例: int と double を受け取る関数
lcvm_register_function_ex("add", ffi_add, 2, "id");

// 例: 2つのポインタを受け取る関数
lcvm_register_function_ex("memcpy", ffi_memcpy, 2, "pp");

型ミスマッチ時のログ出力例:

FFI add: arg 0 type mismatch (expected i, got pointer)

FFI関数シグネチャ

typedef void (*lcvm_ffi_func_t)(LcvmState *vm);

// 戻り値をスタックにプッシュ
void lcvm_push_int(LcvmState *vm, int32_t value);
void lcvm_push_double(LcvmState *vm, double value);

// 引数をスタックからポップ(逆順)
int32_t lcvm_pop_int(LcvmState *vm);
double lcvm_pop_double(LcvmState *vm);

例: 複数引数FFI

// スクリプト: result = add(10, 20.5);
static void ffi_add(LcvmState *vm) {
    double b = lcvm_pop_double(vm);  // 20.5
    int32_t a = lcvm_pop_int(vm);    // 10
    lcvm_push_double(vm, a + b);     // 30.5
}

lcvm_register_function_ex("add", ffi_add, 2, "id");

メモリ管理

メモリレイアウト

Flash/ROM(読み取り専用)

RAM(書き込み可能)

Heap Watermark API

一時的な大量メモリ確保と一括解放のパターンに最適:

// 現在のヒープ位置を記録
size_t mark = lcvm_heap_mark();

// 一時メモリを確保
char *buf = lcvm_malloc(64 * 1024);
// ... 処理 ...

// マーク位置まで一括解放(O(1)、フラグメンテーションなし)
lcvm_heap_release(mark);

スクリプト側からも利用可能:

int mark = heap_mark();
// ... 一時処理 ...
heap_release(mark);

推奨設定

Cortex-M0+(32KB RAM)

#define VM_STACK_SIZE   (8 * 1024)   /* 8KB */
#define VM_GAREA_SIZE   (8 * 1024)   /* 8KB */
#define VM_HEAP_SIZE    (12 * 1024)  /* 12KB */

Cortex-M4 with FPU(64KB RAM)

#define VM_STACK_SIZE   (16 * 1024)  /* 16KB */
#define VM_GAREA_SIZE   (16 * 1024)  /* 16KB */
#define VM_HEAP_SIZE    (24 * 1024)  /* 24KB */

ハードウェアFPU(FPv4-SP)対応により浮動小数点演算が高速化。

RISC-V RV32(RV32IMC)

#define VM_STACK_SIZE   (8 * 1024)   /* 8KB */
#define VM_GAREA_SIZE   (8 * 1024)   /* 8KB */
#define VM_HEAP_SIZE    (12 * 1024)  /* 12KB */

オープンISA。Integer, Multiply, Compressed命令セット対応。

ESP32(320KB RAM)

#define VM_STACK_SIZE   (32 * 1024)  /* 32KB */
#define VM_GAREA_SIZE   (32 * 1024)  /* 32KB */
#define VM_HEAP_SIZE    (128 * 1024) /* 128KB */

静的型モード

-Os オプションで型情報追跡を省略するコンパイルモード。整数演算中心のプログラムで約2倍の高速化を実現します。

lcvmc -Os -oc output.lcbc source.c

制限事項:

ベンチマーク結果と設計の技術背景は アーキテクチャ を参照してください。

デバッグ支援機能

Watchpoint機能

グローバル変数の書き換えを自動検出:

garea 上のオフセット+サイズで監視範囲を指定(サイズは 1/2/4/8 のいずれか)。

// 有効化: LCVM_WATCH_ENABLED=1 でビルド

// 監視対象を追加(最大8個)
lcvm_watch_add(&vm, hp_offset,    sizeof(int), "player_hp");
lcvm_watch_add(&vm, state_offset, sizeof(int), "game_state");

// 監視をクリア
lcvm_watch_clear(&vm);

監視範囲に重なる書き込みが発生すると自動でログ出力されます。

GDB Pretty Printer

tools/gdb_lcvm.py を読み込むと、GDB内でVM状態を人間可読形式で表示:

(gdb) source tools/gdb_lcvm.py
(gdb) p vm
$1 = LcvmState { pc=42, sp=8, error=NONE, ... }

エラー時はコールスタック、レジスタ、オペコード詳細を表示。

Postmortemツール

組み込み環境でのクラッシュ解析に最適:

バッファに書き込んでバイト数を返す API です(最小 24 バイト)。ファイル化・転送は呼び出し側で行います。

// ホスト側でバイナリダンプを出力
unsigned char buf[256];
int len = lcvm_dump_diagnostic_binary(&vm, buf, sizeof(buf));
// 例: buf を Flash / シリアル / ファイルへ送出
# PC側で解析
python tools/lcvm_postmortem.py crash.bin

Simulator SDK

ドメイン特化シミュレータを高速構築するためのフレームワーク。

最小構成

2つのコールバック関数だけでシミュレータを実装:

void on_init(LcvmState *vm, void *user) {
    // FFI関数を登録(型シグネチャ付き)
    lcvm_register_function_ex("set_motor",  ffi_set_motor,  2, "ii");
    lcvm_register_function_ex("get_sensor", ffi_get_sensor, 1, "i");
}

void on_frame(LcvmState *vm, float dt, void *user) {
    // 物理演算
    update_physics(dt);
    // 描画
    render_scene();
}

実装例

demos/raylib_drone/ - RayLib 連携ドローン配送シミュレータ。フルウォークスルーは ドローンデモ を参照。

安全機能

ウォッチドッグタイマー

サンドボックスモード

エラーハンドリング