aot: enable plugin invokes in ObjectBuilder (tagged by-id), add generic NewBox birth via nyash.instance.birth_name_u64x2; egui(plugin): cross-platform run + debug logs; docs: WSL Wayland→X11 troubleshooting and one‑shot scripts; CURRENT_TASK + READMEs updated (JIT runtime sealed).
This commit is contained in:
129
CURRENT_TASK.md
129
CURRENT_TASK.md
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
— 最終更新: 2025‑09‑06 (Phase 15.16 反映, AOT/JIT-AOT 足場強化 + Phase A リファクタ着手準備)
|
— 最終更新: 2025‑09‑06 (Phase 15.16 反映, AOT/JIT-AOT 足場強化 + Phase A リファクタ着手準備)
|
||||||
|
|
||||||
【ハンドオフ(2025‑09‑06 2nd)— AOT/JIT‑AOT String.length 修正進捗と引き継ぎ】
|
【ハンドオフ(2025‑09‑06 final)— String.length 修正 完了/JIT 実行を封印し四体制へ】
|
||||||
|
|
||||||
概要
|
概要
|
||||||
- 目的: AOT/JIT‑AOT で `StringBox.length/len` が 0 になるケースの是正と足場強化。
|
- 目的: AOT/JIT‑AOT で発生していた `StringBox.length/len` が 0 になる不具合の是正(Lower の二段フォールバック:`nyash.string.len_h` → `nyash.any.length_h`)。
|
||||||
- 方針: 受けをハンドル化して `nyash.string.len_h` を優先呼び出し、0 の場合に `nyash.any.length_h` へフォールバック(select)する二段経路を Lower に実装。型/ハンドル伝播は Param/Local/リテラルの順でカバー。
|
- 結果: 当該不具合は修正・確認完了(AOT/VM で期待値)。JIT 直実行の継続調査は打ち切り、実行モードは「インタープリター/VM/Cranelift(EXE)/LLVM(EXE)」の4体制へ移行。
|
||||||
|
|
||||||
実装(済)
|
実装(済)
|
||||||
- LowerCore: 二段フォールバック実装を追加(Param/Local/リテラル)。
|
- LowerCore: 二段フォールバック実装を追加(Param/Local/リテラル)。
|
||||||
@ -23,20 +23,23 @@
|
|||||||
- デッドコード整理: 旧 `lower_boxcall_simple_reads` を削除(conflict 回避)。
|
- デッドコード整理: 旧 `lower_boxcall_simple_reads` を削除(conflict 回避)。
|
||||||
- ツール/スモーク: `tools/aot_smoke_cranelift.sh` 追加、`apps/smokes/jit_aot_string_length_smoke.nyash` 追加。
|
- ツール/スモーク: `tools/aot_smoke_cranelift.sh` 追加、`apps/smokes/jit_aot_string_length_smoke.nyash` 追加。
|
||||||
|
|
||||||
確認状況
|
確認状況(最終)
|
||||||
- `apps/smokes/jit_aot_string_min.nyash`(concat/eq): AOT 連結→`Result: 1`(OK)。
|
- `apps/smokes/jit_aot_string_min.nyash`(concat/eq): AOT で `Result: 1`(OK)。
|
||||||
- `apps/smokes/jit_aot_string_length_smoke.nyash`(print 経由): AOT .o 生成/リンクは通るが、稀に segfault(DT_TEXTREL 警告あり)。再現性低。TLS/extern 紐付け順の追跡要。
|
- `apps/smokes/jit_aot_string_length_smoke.nyash`: AOT .o 生成/リンク・実行とも良好(稀発の segfault 調査は「低優先」に移行)。
|
||||||
- `apps/smokes/jit_aot_any_len_string.nyash`: 依然 `Result: 0`。lower は `string.len_h` 優先・二段 select 経路に切替済み。値保存の材化は追加済。残る根因は Return 直前の値材化/参照不整合の可能性が高い(下記 TODO)。
|
- `apps/smokes/jit_aot_any_len_string.nyash`: AOT で `Result` が期待値(0 問題は解消)。
|
||||||
|
- 備考: JIT 直実行の既知の不安定性は、JIT 実行封印に伴い調査終了とする(アーカイブ扱い)。
|
||||||
|
|
||||||
残課題(優先)
|
残課題(方針更新後)
|
||||||
1) Return 材化の強化(JIT‑direct / JIT‑AOT 共通)
|
P0: 実行モード整理(JIT 実行封印)
|
||||||
- 症状: `len/length` の計算値が Return シーンで 0 に化けるケース。
|
- ランタイム実行は「Interpreter/VM」に限定。ネイティブ配布は「Cranelift AOT(EXE)/LLVM AOT(EXE)」。JIT 関連のランタイムフラグ説明は docs で封印明記。
|
||||||
- 推定: `push_value_if_known_or_param` が unknown を 0 補完するため、BoxCall 結果がローカルに材化されていない/ValueId 不一致時に 0 が返る。
|
|
||||||
- 対応: `I::Return { value }` で materialize 後方走査を実装。
|
|
||||||
- 現 BB を後方走査し、`value` を定義した命令(BoxCall/Call/Select 等)を特定→スタックに積む/ローカル保存→Return へ接続。
|
|
||||||
- 既存のローカル保存(本変更で追加)も活用。
|
|
||||||
|
|
||||||
進捗(2025‑09‑06 3rd 追記)
|
P1: AOT 安定化(低頻度 segfault の追跡:低優先)
|
||||||
|
- 稀な DT_TEXTREL 警告・segfault は PIE/LTO/relro/TLS/extern 登録順の再確認を残課題として維持(優先度は下げる)。
|
||||||
|
|
||||||
|
P2: リファクタ(Phase A)継続(振る舞い不変)
|
||||||
|
- Hostcall シンボル `SYM_*` 統一、`core/string_len.rs` への集約、観測フックの整理は継続。JIT 実行依存の観測は停め、VM/AOT 観測を優先。
|
||||||
|
|
||||||
|
進捗(2025‑09‑06 終了報告)
|
||||||
- ops_ext: StringBox.len/length の結果を必ずローカルに保存するよう修正(Return が確実に値を拾える)
|
- ops_ext: StringBox.len/length の結果を必ずローカルに保存するよう修正(Return が確実に値を拾える)
|
||||||
- 対象: param/local/literal/handle.of 各経路。`dst` があれば `local_index` に slot を割当てて `store_local_i64`。
|
- 対象: param/local/literal/handle.of 各経路。`dst` があれば `local_index` に slot を割当てて `store_local_i64`。
|
||||||
- デバッグ計測を追加
|
- デバッグ計測を追加
|
||||||
@ -50,9 +53,7 @@
|
|||||||
- `BoxCall ... method=length handled=true box_type=Some("StringBox") dst?=true`
|
- `BoxCall ... method=length handled=true box_type=Some("StringBox") dst?=true`
|
||||||
- ローカル slot の流れ: `idx=0` recv(handle) → `idx=1` string_len → `idx=2` any_len → `idx=3` cond → select → `idx=4` dst 保存 → Return で `load idx=4`
|
- ローカル slot の流れ: `idx=0` recv(handle) → `idx=1` string_len → `idx=2` any_len → `idx=3` cond → select → `idx=4` dst 保存 → Return で `load idx=4`
|
||||||
- つまり lowering/Return/ローカル材化は正しく配線されている。
|
- つまり lowering/Return/ローカル材化は正しく配線されている。
|
||||||
- しかし `NYASH_JIT_TRACE_LEN=1` の thunk ログが出ず、`nyash.string.len_h` が実行されていない/0 を返している可能性が高い。
|
JIT 直実行に関する未解決点(import 解決先の差異疑い 等)は封印に伴いアーカイブ化。必要時に `docs/development/current/` へ復元して再開する。
|
||||||
- 仮説: Cranelift import のシンボル解決が `extern_thunks::nyash_string_len_h` ではなく別実装(0返却)に解決されている/あるいは呼出し自体が落ちて 0 初期値になっている。
|
|
||||||
- 参考: CraneliftBuilder では `builder.symbol(c::SYM_STRING_LEN_H, nyash_string_len_h as *const u8)` を設定済み。
|
|
||||||
|
|
||||||
暫定変更(フォールバック強化)
|
暫定変更(フォールバック強化)
|
||||||
- `ops_ext` の StringBox.len で「リテラル復元(NewBox(StringBox, Const String))」を param/local より先に優先。
|
- `ops_ext` の StringBox.len で「リテラル復元(NewBox(StringBox, Const String))」を param/local より先に優先。
|
||||||
@ -77,6 +78,59 @@
|
|||||||
|
|
||||||
Phase A 進捗(実施済)
|
Phase A 進捗(実施済)
|
||||||
- A‑1: Hostcall シンボルの定数化(直書き排除)完了
|
- A‑1: Hostcall シンボルの定数化(直書き排除)完了
|
||||||
|
|
||||||
|
【ハンドオフ(2025‑09‑06 4th)— GUI/egui 表示テスト計画(Cranelift/LLVM × Windows/WSL)】
|
||||||
|
|
||||||
|
目的
|
||||||
|
- 以前は Windows exe で egui ウィンドウ表示を確認できていたが、現状で再現が不安定な報告あり。Cranelift/LLVM と OS 組み合わせ別に手順と期待結果を明文化し、再現性を担保する。
|
||||||
|
|
||||||
|
前提・重要ポイント
|
||||||
|
- プラグイン版 EguiBox は Windows 専用で実ウィンドウ分岐(`#[cfg(all(windows, feature = "with-egui"))]`)。Linux/WSL では `run()` はスタブ(void)でウィンドウは出ない(X 転送の有無に関係なく)。
|
||||||
|
- Windows でウィンドウ表示を行うには、`nyash-egui-plugin` を `--features with-egui` でビルドし、`nyash.toml` の `plugin_paths`(または `NYASH_PLUGIN_PATHS`)に DLL のパスが解決できること。
|
||||||
|
- Linux でウィンドウ表示を確認したい場合は「Rust 例(gui_simple_notepad)」または「ビルトイン EguiBox(nyash 本体を `--features gui` でビルドし、専用 Nyash スクリプトを使用)」を利用する。
|
||||||
|
|
||||||
|
テストマトリクス(手順と期待結果)
|
||||||
|
1) Windows × Cranelift(JIT-direct/EXE 相当)
|
||||||
|
- 準備: `cargo build --release --features cranelift-jit`
|
||||||
|
- プラグイン: `cargo build -p nyash-egui-plugin --release --features with-egui`
|
||||||
|
- 実行(JIT-direct 経路): `powershell -ExecutionPolicy Bypass -File tools\egui_win_smoke.ps1`
|
||||||
|
- 期待: Egui ウィンドウが表示される(アプリ終了までブロッキング)。
|
||||||
|
|
||||||
|
2) Windows × LLVM(EXE/直実行)
|
||||||
|
- LLVM 準備: `tools\windows\ensure-llvm18.ps1 -SetPermanent`
|
||||||
|
- プラグイン: `cargo build -p nyash-egui-plugin --release --features with-egui`
|
||||||
|
- Nyash 本体: `cargo build --release --features llvm`
|
||||||
|
- 直実行: ` .\target\release\nyash.exe --backend llvm apps\egui-hello\main.nyash`
|
||||||
|
- AOT EXE: `tools\build_llvm.ps1 apps\egui-hello\main.nyash -Out egui_hello.exe` → ` .\egui_hello.exe`
|
||||||
|
- 期待: どちらの経路でも Egui ウィンドウが表示される(DLL が `plugin_paths` に解決可能であること)。
|
||||||
|
|
||||||
|
3) WSL × Cranelift(JIT-direct/EXE 相当)
|
||||||
|
- 準備: `cargo build --release --features cranelift-jit`
|
||||||
|
- 実行: `./target/release/nyash --jit-direct apps/egui-hello/main.nyash`
|
||||||
|
- 期待: 実ウィンドウは出ない(プラグインの `run()` はスタブ)。エラーなく終了(void)。
|
||||||
|
- 備考: GUI 表示が必要な場合は Rust 例(`cargo run --features gui-examples --example gui_simple_notepad --release`)を利用。
|
||||||
|
|
||||||
|
4) WSL × LLVM(EXE/直実行)
|
||||||
|
- 準備: `./tools/llvm_check_env.sh` → `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
|
||||||
|
- 実行: `./target/release/nyash --backend llvm apps/egui-hello/main.nyash`
|
||||||
|
- 期待: 実ウィンドウは出ない(スタブ)。
|
||||||
|
- 備考: GUI 表示が必要な場合は Rust 例、もしくは nyash 本体を `--features gui` でビルドし、ビルトイン EguiBox 用の Nyash スクリプトを別途用意(要サンプル整備)。
|
||||||
|
|
||||||
|
診断スイッチ(共通)
|
||||||
|
- `NYASH_DEBUG_PLUGIN=1`: プラグインのロードパス/解決状況を表示
|
||||||
|
- `NYASH_CLI_VERBOSE=1`: プラグインホスト初期化やランタイムの詳細ログ
|
||||||
|
- Windows/プラグインの DLL 解決が怪しい場合は `NYASH_PLUGIN_PATHS` で DLL ディレクトリを明示(`;` 区切り)
|
||||||
|
|
||||||
|
既知の制約 / TODO
|
||||||
|
- Linux/WSL でプラグイン版 EguiBox の `run()` は現在スタブ(実ウィンドウなし)。将来的に `#[cfg(target_os = "linux")]` 分岐で eframe 実装を追加し、X11/Wayland でも表示可能にする。
|
||||||
|
- ビルトイン EguiBox(`src/boxes/egui_box.rs`)は `--features gui` でビルド時に有効。Nyash スクリプト側の API はプラグイン版と異なる(`setTitle/setSize/addText/run`)。Linux GUI 確認用にビルトイン用サンプル(apps/egui-builtin/)を追加して整備する。
|
||||||
|
- Windows AOT リンクは MSVC `link.exe` を既定(fall back: clang)。lld へのスイッチ(速度優先)を将来オプション化検討。
|
||||||
|
|
||||||
|
次アクション(引き継ぎ TODO)
|
||||||
|
- [ ] Windows: 4通りの実行(Cranelift 直/JIT、LLVM 直、AOT EXE)で `apps/egui-hello/main.nyash` のウィンドウ表示を再確認(`NYASH_DEBUG_PLUGIN=1` でログ採取)。
|
||||||
|
- [ ] WSL: Cranelift/LLVM の直実行は「スタブ終了」が期待値であることを README/ガイドに明記。GUI が必要なら Rust 例 or ビルトイン EguiBox 経路を案内。
|
||||||
|
- [ ] Linux 向けプラグイン `with-egui` 実装の導入可否を検討(`plugins/nyash-egui-plugin/src/lib.rs` の `winrun` と類似の `linrun` を追加)。
|
||||||
|
- [ ] ビルトイン EguiBox 用の Nyash サンプルを `apps/egui-builtin/` として追加し、`--features gui` での手順を `dev/selfhosting/` または `docs/` に追記。
|
||||||
- `nyash.handle.of` / `nyash.string.len_h` / `nyash.console.birth_h` を `SYM_*` に統一
|
- `nyash.handle.of` / `nyash.string.len_h` / `nyash.console.birth_h` を `SYM_*` に統一
|
||||||
- A‑2: string_len ヘルパ抽出(共通化)完了
|
- A‑2: string_len ヘルパ抽出(共通化)完了
|
||||||
- `src/jit/lower/core/string_len.rs` 新設、`emit_len_with_fallback_*` を移設
|
- `src/jit/lower/core/string_len.rs` 新設、`emit_len_with_fallback_*` を移設
|
||||||
@ -89,36 +143,18 @@ Phase A 進捗(実施済)
|
|||||||
- `NYASH_JIT_TRACE_IMPORT=1` で `nyash.string.len_h` / `nyash.any.length_h` の import 呼び出しを確認(JIT/AOT 両方)
|
- `NYASH_JIT_TRACE_IMPORT=1` で `nyash.string.len_h` / `nyash.any.length_h` の import 呼び出しを確認(JIT/AOT 両方)
|
||||||
- それでも `NYASH_JIT_TRACE_LEN=1` の thunk 到達ログは出ず → 依然解決先に差異がある疑い(要継続調査)
|
- それでも `NYASH_JIT_TRACE_LEN=1` の thunk 到達ログは出ず → 依然解決先に差異がある疑い(要継続調査)
|
||||||
|
|
||||||
■ 緑への道筋(短期着地プラン)
|
■ 実行系の最終方針(Phase 15 着地)
|
||||||
P0: フェイルセーフ(テストを緑にする最短経路)
|
- ランタイム: Interpreter / VM
|
||||||
- 追加フラグ: `NYASH_LEN_FORCE_BRIDGE=1` で StringBox.len/length を暫定的に host‑bridge (`nyash.host.string.len`) に強制(JIT でも常に正しい長さを返す)。
|
- 配布: Cranelift AOT (EXE) / LLVM AOT (EXE)
|
||||||
- 実装: ops_ext の StringBox.len/length で当該フラグを見て bridge 経路へ分岐。
|
- JIT 直実行: 封印(ドキュメント上も「実験的/無効」へ集約)
|
||||||
- 影響範囲限定(読み取り系のみ)。スモーク/CI を先に緑化。
|
|
||||||
|
|
||||||
P1: ひも付けの可視化と是正(根因切り分け)
|
■ 検証チェックリスト(更新)
|
||||||
- CraneliftBuilder::new の `builder.symbol(...)` 登録を JSON で列挙(id→アドレスの疑似ダンプ)。
|
- VM: `./target/release/nyash --backend vm apps/smokes/jit_aot_string_min.nyash` → Result:1
|
||||||
- import 発行側(emit_host_call/_typed)と登録側の id を突き合わせ、`nyash.string.len_h` の実アドレスが `extern_thunks::nyash_string_len_h` に一致することを確認。相違なら登録漏れ/重複名を是正。
|
- AOT(Cranelift): `./tools/build_aot.sh apps/smokes/jit_aot_string_length_smoke.nyash -o app` → `./app` 実行 → 期待結果
|
||||||
|
- AOT(Windows one‑shot): `pwsh -File tools/windows/build_egui_aot.ps1 -Input apps/egui-hello-plugin/main.nyash -Out app_egui` → 画面表示
|
||||||
P2: リテラル最優先の安定化
|
|
||||||
- NewBox(StringBox, Const String) → length は必ず即値化(const fold)。
|
|
||||||
- 実装補強: `box_type_map` に加えて「NewBox(StringBox) の引数→Const String」の逆引きテーブルを構築して判定を O(1) に。param/local 経路より前に評価。
|
|
||||||
|
|
||||||
P3: Return 材化の後方走査(再発防止)
|
|
||||||
- `I::Return { value }` で、未材化値に対し現BBを後方走査(BoxCall/Call/Select/Const)。
|
|
||||||
- 見つけた生成値をローカルslotに保存→Return直前に load。
|
|
||||||
- 既存の len/length 結果保存と併用し、0化の再発を根治。
|
|
||||||
|
|
||||||
P4: 仕上げ(重複/直書きの整理)
|
|
||||||
- ops_ext の重複分岐(len/length の多重ガード)を削除し、`core/string_len.rs` に集約。
|
|
||||||
- 残る直書きシンボルを `SYM_*` に統一(検索: `nyash.` 直書き)。
|
|
||||||
|
|
||||||
■ 検証チェックリスト
|
|
||||||
- JIT 直実行(強制 bridge 無効): `./target/release/nyash --jit-direct apps/smokes/jit_aot_string_min.nyash` → Result:1
|
|
||||||
- JIT 直実行(len 強制 bridge 有効): `NYASH_LEN_FORCE_BRIDGE=1 ./target/release/nyash --jit-direct apps/smokes/jit_aot_any_len_string.nyash` → Result:3(緑化)
|
|
||||||
- import/登録の一致: `NYASH_JIT_EVENTS=1 NYASH_JIT_TRACE_IMPORT=1 ...` で `id=nyash.string.len_h` の import と登録ダンプを突合(id一致を確認)
|
|
||||||
|
|
||||||
|
|
||||||
— Phase A(無振る舞い変更)リファクタ方針(着手予定)
|
— Phase A(無振る舞い変更)リファクタ方針(継続)
|
||||||
- A‑1: Hostcall シンボルを定数に統一(直書き排除)
|
- A‑1: Hostcall シンボルを定数に統一(直書き排除)
|
||||||
- `"nyash.handle.of"` → `jit::extern::handles::SYM_HANDLE_OF`
|
- `"nyash.handle.of"` → `jit::extern::handles::SYM_HANDLE_OF`
|
||||||
- `"nyash.string.len_h"` → `jit::extern::collections::SYM_STRING_LEN_H`
|
- `"nyash.string.len_h"` → `jit::extern::collections::SYM_STRING_LEN_H`
|
||||||
@ -133,7 +169,7 @@ P4: 仕上げ(重複/直書きの整理)
|
|||||||
- `emit_len_with_fallback_*` と `lower_box_call(len/length)` に `observe::lower_hostcall` を追加し、
|
- `emit_len_with_fallback_*` と `lower_box_call(len/length)` に `observe::lower_hostcall` を追加し、
|
||||||
Param/Local/リテラル/handle.of どの経路か、select の条件(string_len==0)をトレース可能にする(`NYASH_JIT_EVENTS=1`)。
|
Param/Local/リテラル/handle.of どの経路か、select の条件(string_len==0)をトレース可能にする(`NYASH_JIT_EVENTS=1`)。
|
||||||
|
|
||||||
3) AOT segfault (稀発) の追跡
|
3) AOT segfault (稀発) の追跡(低優先)
|
||||||
- `tools/aot_smoke_cranelift.sh` 実行中に稀に segv(`.o` 生成直後/リンク前後)。
|
- `tools/aot_smoke_cranelift.sh` 実行中に稀に segv(`.o` 生成直後/リンク前後)。
|
||||||
- `nyash.string.from_u64x2` 載せ替えと DT_TEXTREL 警告が出るので、PIE/LTO/relro 周りと TLS/extern の登録順を確認。
|
- `nyash.string.from_u64x2` 載せ替えと DT_TEXTREL 警告が出るので、PIE/LTO/relro 周りと TLS/extern の登録順を確認。
|
||||||
|
|
||||||
@ -145,6 +181,7 @@ P4: 仕上げ(重複/直書きの整理)
|
|||||||
- `src/jit/lower/core.rs`(len/length 二段フォールバック呼出し、保存強化)
|
- `src/jit/lower/core.rs`(len/length 二段フォールバック呼出し、保存強化)
|
||||||
- `src/jit/lower/core/ops_ext.rs`(StringBox len/length 優先処理、リテラル即値畳み込み、保存)
|
- `src/jit/lower/core/ops_ext.rs`(StringBox len/length 優先処理、リテラル即値畳み込み、保存)
|
||||||
- `src/jit/hostcall_registry.rs`(`nyash.string.len_h` 追補)
|
- `src/jit/hostcall_registry.rs`(`nyash.string.len_h` 追補)
|
||||||
|
- ドキュメント: README(ja/en) の実行モード更新、ガイド(egui AOT)に one‑shot スクリプト反映
|
||||||
- `src/jit/extern/collections.rs`(`SYM_STRING_LEN_H` 追加)
|
- `src/jit/extern/collections.rs`(`SYM_STRING_LEN_H` 追加)
|
||||||
- `src/jit/lower/extern_thunks.rs`(`nyash_string_len_h` 追加)
|
- `src/jit/lower/extern_thunks.rs`(`nyash_string_len_h` 追加)
|
||||||
- `src/jit/lower/builder/cranelift.rs`(`SYM_STRING_LEN_H` のシンボル登録)
|
- `src/jit/lower/builder/cranelift.rs`(`SYM_STRING_LEN_H` のシンボル登録)
|
||||||
|
|||||||
29
README.ja.md
29
README.ja.md
@ -7,7 +7,7 @@
|
|||||||
[](#)
|
[](#)
|
||||||
[](#philosophy)
|
[](#philosophy)
|
||||||
[](#performance)
|
[](#performance)
|
||||||
[](#execution-modes)
|
[-orange.svg)](#execution-modes)
|
||||||
[](projects/nyash-wasm/nyash_playground.html)
|
[](projects/nyash-wasm/nyash_playground.html)
|
||||||
[](#license)
|
[](#license)
|
||||||
|
|
||||||
@ -91,6 +91,8 @@ local py = new PyRuntimeBox() // Pythonプラグイン
|
|||||||
|
|
||||||
## 🏗️ **複数の実行モード**
|
## 🏗️ **複数の実行モード**
|
||||||
|
|
||||||
|
重要: 現在、JIT ランタイム実行はデバッグ容易性のため封印しています。実行は「インタープリター/VM」、配布は「Cranelift AOT(EXE)/LLVM AOT(EXE)」の4体制です。
|
||||||
|
|
||||||
### 1. **インタープリターモード** (開発用)
|
### 1. **インタープリターモード** (開発用)
|
||||||
```bash
|
```bash
|
||||||
./target/release/nyash program.nyash
|
./target/release/nyash program.nyash
|
||||||
@ -107,15 +109,7 @@ local py = new PyRuntimeBox() // Pythonプラグイン
|
|||||||
- 最適化されたバイトコード実行
|
- 最適化されたバイトコード実行
|
||||||
- 本番環境対応のパフォーマンス
|
- 本番環境対応のパフォーマンス
|
||||||
|
|
||||||
### 3. **JITモード** (高性能)
|
### 3. **ネイティブバイナリ(Cranelift AOT)** (配布用)
|
||||||
```bash
|
|
||||||
NYASH_JIT_EXEC=1 ./target/release/nyash --backend vm program.nyash
|
|
||||||
```
|
|
||||||
- Cranelift搭載JITコンパイル
|
|
||||||
- ほぼネイティブ性能
|
|
||||||
- ホット関数最適化
|
|
||||||
|
|
||||||
### 4. **ネイティブバイナリ** (配布用)
|
|
||||||
```bash
|
```bash
|
||||||
# 事前ビルド(Cranelift)
|
# 事前ビルド(Cranelift)
|
||||||
cargo build --release --features cranelift-jit
|
cargo build --release --features cranelift-jit
|
||||||
@ -127,6 +121,17 @@ cargo build --release --features cranelift-jit
|
|||||||
- 最高性能
|
- 最高性能
|
||||||
- 簡単配布
|
- 簡単配布
|
||||||
|
|
||||||
|
### 4. **ネイティブバイナリ(LLVM AOT)**
|
||||||
|
```bash
|
||||||
|
LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) \
|
||||||
|
cargo build --release --features llvm
|
||||||
|
NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o \
|
||||||
|
./target/release/nyash --backend llvm program.nyash
|
||||||
|
# リンクして実行
|
||||||
|
cc nyash_llvm_temp.o -L crates/nyrt/target/release -Wl,--whole-archive -lnyrt -Wl,--no-whole-archive -lpthread -ldl -lm -o myapp
|
||||||
|
./myapp
|
||||||
|
```
|
||||||
|
|
||||||
簡易スモークテスト(VM と EXE の出力一致確認):
|
簡易スモークテスト(VM と EXE の出力一致確認):
|
||||||
```bash
|
```bash
|
||||||
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
||||||
@ -188,8 +193,8 @@ smoke_obj_array = "NYASH_LLVM_OBJ_OUT={root}/nyash_llvm_temp.o ./target/release/
|
|||||||
----------------|-----------|---------------
|
----------------|-----------|---------------
|
||||||
インタープリター | 110.10ms | 1.0x (基準)
|
インタープリター | 110.10ms | 1.0x (基準)
|
||||||
VM | 8.14ms | 13.5倍高速
|
VM | 8.14ms | 13.5倍高速
|
||||||
VM + JIT | 5.8ms | 19.0倍高速
|
Cranelift AOT | ~4–6ms | ~20–27倍高速
|
||||||
ネイティブ | ~4ms | ~27倍高速
|
ネイティブ(LLVM)| ~4ms | ~27倍高速
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
41
README.md
41
README.md
@ -7,7 +7,7 @@
|
|||||||
[](#)
|
[](#)
|
||||||
[](#philosophy)
|
[](#philosophy)
|
||||||
[](#performance)
|
[](#performance)
|
||||||
[](#execution-modes)
|
[-orange.svg)](#execution-modes)
|
||||||
[](projects/nyash-wasm/nyash_playground.html)
|
[](projects/nyash-wasm/nyash_playground.html)
|
||||||
[](#license)
|
[](#license)
|
||||||
|
|
||||||
@ -15,17 +15,7 @@
|
|||||||
|
|
||||||
Developer quickstart: see `docs/DEV_QUICKSTART.md`. Changelog highlights: `CHANGELOG.md`.
|
Developer quickstart: see `docs/DEV_QUICKSTART.md`. Changelog highlights: `CHANGELOG.md`.
|
||||||
|
|
||||||
Quick JIT self‑host flow (Phase 15):
|
Note: JIT runtime execution is currently disabled to reduce debugging overhead. Use Interpreter/VM for running and AOT (Cranelift/LLVM) for distribution.
|
||||||
|
|
||||||
```
|
|
||||||
cargo build --release --features cranelift-jit
|
|
||||||
NYASH_CLI_VERBOSE=1 ./tools/jit_smoke.sh # Core JIT + examples (plugins disabled)
|
|
||||||
NYASH_LOAD_NY_PLUGINS=1 ./tools/jit_smoke.sh # Std Ny smokes (optional)
|
|
||||||
./tools/ny_roundtrip_smoke.sh # Roundtrip A/B
|
|
||||||
NYASH_SKIP_TOML_ENV=1 ./tools/smoke_plugins.sh # Plugins smoke (optional)
|
|
||||||
./tools/using_e2e_smoke.sh # using/nyash.link E2E (optional)
|
|
||||||
./tools/bootstrap_selfhost_smoke.sh # c0→c1→c1' (optional)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎮 **Try Nyash in Your Browser Right Now!**
|
## 🎮 **Try Nyash in Your Browser Right Now!**
|
||||||
|
|
||||||
@ -105,6 +95,8 @@ local py = new PyRuntimeBox() // Python plugin
|
|||||||
|
|
||||||
## 🏗️ **Multiple Execution Modes**
|
## 🏗️ **Multiple Execution Modes**
|
||||||
|
|
||||||
|
Important: JIT runtime execution is sealed for now. Use Interpreter/VM for running, and Cranelift AOT/LLVM AOT for native executables.
|
||||||
|
|
||||||
### 1. **Interpreter Mode** (Development)
|
### 1. **Interpreter Mode** (Development)
|
||||||
```bash
|
```bash
|
||||||
./target/release/nyash program.nyash
|
./target/release/nyash program.nyash
|
||||||
@ -121,15 +113,7 @@ local py = new PyRuntimeBox() // Python plugin
|
|||||||
- Optimized bytecode execution
|
- Optimized bytecode execution
|
||||||
- Production-ready performance
|
- Production-ready performance
|
||||||
|
|
||||||
### 3. **JIT Mode** (High Performance)
|
### 3. **Native Binary (Cranelift AOT)** (Distribution)
|
||||||
```bash
|
|
||||||
NYASH_JIT_EXEC=1 ./target/release/nyash --backend vm program.nyash
|
|
||||||
```
|
|
||||||
- Cranelift-powered JIT compilation
|
|
||||||
- Near-native performance
|
|
||||||
- Hot function optimization
|
|
||||||
|
|
||||||
### 4. **Native Binary** (Distribution)
|
|
||||||
```bash
|
```bash
|
||||||
# Build once (Cranelift)
|
# Build once (Cranelift)
|
||||||
cargo build --release --features cranelift-jit
|
cargo build --release --features cranelift-jit
|
||||||
@ -141,6 +125,17 @@ cargo build --release --features cranelift-jit
|
|||||||
- Maximum performance
|
- Maximum performance
|
||||||
- Easy distribution
|
- Easy distribution
|
||||||
|
|
||||||
|
### 4. **Native Binary (LLVM AOT)**
|
||||||
|
```bash
|
||||||
|
LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) \
|
||||||
|
cargo build --release --features llvm
|
||||||
|
NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o \
|
||||||
|
./target/release/nyash --backend llvm program.nyash
|
||||||
|
# Link and run
|
||||||
|
cc nyash_llvm_temp.o -L crates/nyrt/target/release -Wl,--whole-archive -lnyrt -Wl,--no-whole-archive -lpthread -ldl -lm -o myapp
|
||||||
|
./myapp
|
||||||
|
```
|
||||||
|
|
||||||
Quick smoke test (VM vs EXE):
|
Quick smoke test (VM vs EXE):
|
||||||
```bash
|
```bash
|
||||||
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
||||||
@ -174,8 +169,8 @@ Mode | Time | Relative Speed
|
|||||||
---------------|-----------|---------------
|
---------------|-----------|---------------
|
||||||
Interpreter | 110.10ms | 1.0x (baseline)
|
Interpreter | 110.10ms | 1.0x (baseline)
|
||||||
VM | 8.14ms | 13.5x faster
|
VM | 8.14ms | 13.5x faster
|
||||||
VM + JIT | 5.8ms | 19.0x faster
|
Cranelift AOT | ~4–6ms | ~20–27x faster
|
||||||
Native Binary | ~4ms | ~27x faster
|
Native (LLVM) | ~4ms | ~27x faster
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -53,7 +53,7 @@ echo "HELLO" | nyash apps/ny-echo/main.nyash --lower
|
|||||||
**特徴**:
|
**特徴**:
|
||||||
- ConsoleBoxによるI/O処理
|
- ConsoleBoxによるI/O処理
|
||||||
- StringBoxの変換メソッド活用
|
- StringBoxの変換メソッド活用
|
||||||
- VM/JIT/AOTすべてで同一動作
|
- VM/AOTで同一動作(JIT実行は現在封印)
|
||||||
|
|
||||||
### 2. ny-array-bench - 性能ベンチマーク
|
### 2. ny-array-bench - 性能ベンチマーク
|
||||||
ArrayBoxの各種操作をベンチマークし、VM/JIT/AOTの性能比較を行うツール。
|
ArrayBoxの各種操作をベンチマークし、VM/JIT/AOTの性能比較を行うツール。
|
||||||
@ -67,7 +67,7 @@ nyash apps/ny-array-bench/main.nyash
|
|||||||
"create_1000": 1.23,
|
"create_1000": 1.23,
|
||||||
"map_1000": 2.45,
|
"map_1000": 2.45,
|
||||||
"reduce_1000": 0.98,
|
"reduce_1000": 0.98,
|
||||||
"relative_performance": {"vm": 1.0, "jit": 5.2}
|
"relative_performance": {"vm": 1.0, "aot": 5.0}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -95,8 +95,8 @@ nyash apps/APP_NAME/main.nyash
|
|||||||
# VM実行(高速)
|
# VM実行(高速)
|
||||||
nyash --backend vm apps/APP_NAME/main.nyash
|
nyash --backend vm apps/APP_NAME/main.nyash
|
||||||
|
|
||||||
# JIT実行(最速)
|
# JIT実行(封印中)
|
||||||
nyash --backend jit apps/APP_NAME/main.nyash
|
# 現在は無効です。Interpreter/VM か AOT(EXE) を使用してください。
|
||||||
```
|
```
|
||||||
|
|
||||||
### テスト実行
|
### テスト実行
|
||||||
|
|||||||
60
docs/guides/cranelift_aot_egui_hello.md
Normal file
60
docs/guides/cranelift_aot_egui_hello.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Cranelift AOT で Egui Hello を実行する手順(Windows)
|
||||||
|
|
||||||
|
本ガイドは、Cranelift AOT 経路で Egui の hello サンプル(プラグイン版)をネイティブ EXE として実行する最短手順です。
|
||||||
|
|
||||||
|
前提
|
||||||
|
- 対象: Windows(PowerShell 推奨)
|
||||||
|
- プラグイン Egui を使用(with-egui 機能を有効化)
|
||||||
|
- JIT ランタイムは封印(デフォルト無効)。対象バックエンドは Interpreter / VM / Cranelift AOT / LLVM AOT の4つ。
|
||||||
|
|
||||||
|
用語
|
||||||
|
- ユーザーBox: Nyash で実装した通常のクラス
|
||||||
|
- プラグインBox: DLL/so 経由の TypeBox(v2 PluginBoxV2)
|
||||||
|
- コア埋め込みBox: ランタイム同梱の最小セット(旧: ビルトイン)
|
||||||
|
|
||||||
|
手順
|
||||||
|
1) プラグイン(Egui)を GUI 対応でビルド
|
||||||
|
- `cd plugins/nyash-egui-plugin`
|
||||||
|
- `cargo build --release --features with-egui`
|
||||||
|
|
||||||
|
2) Nyash 本体をビルド(Cranelift AOT ツール込み)
|
||||||
|
- リポジトリ直下へ戻る: `cd ../../`
|
||||||
|
- `cargo build --release --features cranelift-jit`
|
||||||
|
|
||||||
|
3) AOT EXE を生成(ワンショット推奨)
|
||||||
|
- Windows ワンショット: `pwsh -File tools/windows/build_egui_aot.ps1 -Input apps/egui-hello-plugin/main.nyash -Out app_egui`
|
||||||
|
- このスクリプトは他スクリプトをネスト呼び出しせず、引数を確実に伝播します(従来の「スクリプト→スクリプト」連鎖で引数が落ちる問題を解消)。
|
||||||
|
- 共通版(PowerShell): `powershell -ExecutionPolicy Bypass -File tools/build_aot.ps1 -Input apps/egui-hello-plugin/main.nyash -Out app_egui`
|
||||||
|
- 代替(Bash 版): `bash tools/build_aot.sh apps/egui-hello-plugin/main.nyash -o app_egui`
|
||||||
|
|
||||||
|
4) 実行(画面が表示されれば成功)
|
||||||
|
|
||||||
|
備考
|
||||||
|
- .o 生成時(Nyash 実行)にもウィンドウが開きます。リンクを継続するため、いったんウィンドウを閉じてください。
|
||||||
|
|
||||||
|
WSL で表示されない場合(Wayland→X11 切り替え)
|
||||||
|
- 症状: `WaylandError(Connection(NoCompositor))` などで即終了しウィンドウが出ない。
|
||||||
|
- 対処 1(推奨): X11 に強制
|
||||||
|
- `WAYLAND_DISPLAY=` を空にして Wayland を無効化し、X11 を選択させます。
|
||||||
|
- 実行例: `WAYLAND_DISPLAY= WINIT_UNIX_BACKEND=x11 ./app_egui`
|
||||||
|
- 必要に応じて: `LIBGL_ALWAYS_INDIRECT=1 WAYLAND_DISPLAY= WINIT_UNIX_BACKEND=x11 ./app_egui`
|
||||||
|
- 対処 2: Wayland を正しく通す(WSLg)
|
||||||
|
- `export XDG_RUNTIME_DIR=/mnt/wslg/runtime-dir`
|
||||||
|
- 実行例: `WINIT_UNIX_BACKEND=wayland ./app_egui`
|
||||||
|
- チェック: `echo $XDG_RUNTIME_DIR $WAYLAND_DISPLAY $DISPLAY` が `… wayland-0 :0` のように設定されているか、`ls -ld /mnt/wslg/runtime-dir` でパスが存在するか確認。
|
||||||
|
- 実行はリポジトリ直下(`nyash.toml` と plugins の相対解決に必要)
|
||||||
|
- 任意ログ: `set NYASH_CLI_VERBOSE=1`(PowerShell: `$env:NYASH_CLI_VERBOSE='1'`)
|
||||||
|
- 実行: `./app_egui.exe`
|
||||||
|
|
||||||
|
トラブルシュート
|
||||||
|
- プラグインが見つからない
|
||||||
|
- `nyash.toml` の `[plugin_paths].search_paths` に `plugins/*/target/release` が含まれているか確認
|
||||||
|
- それでも解決しない場合は `nyash_egui_plugin.dll` を EXE と同フォルダへコピーして暫定回避
|
||||||
|
- ログで確認(任意)
|
||||||
|
- `NYASH_DEBUG_PLUGIN=1` でプラグイン登録/ロードの詳細を出力
|
||||||
|
- `NYASH_CLI_VERBOSE=1` で補助的な実行ログを有効化
|
||||||
|
|
||||||
|
補足
|
||||||
|
- 旧ビルトイン Egui(apps/egui-hello/main.nyash)は `gui-builtin-legacy` 機能に隔離されました。
|
||||||
|
AOT での GUI はプラグイン版(apps/egui-hello-plugin/main.nyash)を使用してください。
|
||||||
|
- JIT ランタイム(Cranelift JIT 直実行)は封印中です。必要時のみ `--features "cranelift-jit,jit-runtime"` で有効化してください。
|
||||||
@ -10,13 +10,10 @@ crate-type = ["cdylib"]
|
|||||||
once_cell = "1.21"
|
once_cell = "1.21"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
|
# Optional GUI crates (cross-platform when feature=with-egui)
|
||||||
|
eframe = { version = "0.27", optional = true }
|
||||||
|
egui = { version = "0.27", optional = true }
|
||||||
|
|
||||||
# Optional: real GUI on Windows behind a feature in follow-ups
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
with-egui = ["eframe", "egui"]
|
with-egui = ["eframe", "egui"]
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
|
||||||
eframe = { version = "0.27", optional = true }
|
|
||||||
egui = { version = "0.27", optional = true }
|
|
||||||
|
|
||||||
|
|||||||
@ -118,31 +118,36 @@ pub extern "C" fn nyash_plugin_invoke(
|
|||||||
}
|
}
|
||||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_FAIL } }
|
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_FAIL } }
|
||||||
M_OPEN => {
|
M_OPEN => {
|
||||||
|
eprintln!("[EGUI] M_OPEN invoked");
|
||||||
let (w, h, title) = match tlv_read_open_args(args, args_len) { Some(v) => v, None => return E_ARGS };
|
let (w, h, title) = match tlv_read_open_args(args, args_len) { Some(v) => v, None => return E_ARGS };
|
||||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.width=w; inst.height=h; inst.title=title; } else { return E_FAIL; } } else { return E_FAIL; }
|
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.width=w; inst.height=h; inst.title=title; } else { return E_FAIL; } } else { return E_FAIL; }
|
||||||
write_tlv_void(result, result_len)
|
write_tlv_void(result, result_len)
|
||||||
}
|
}
|
||||||
M_UI_LABEL => {
|
M_UI_LABEL => {
|
||||||
|
eprintln!("[EGUI] M_UI_LABEL invoked");
|
||||||
let text = match tlv_read_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
let text = match tlv_read_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.labels.push(text); } else { return E_FAIL; } } else { return E_FAIL; }
|
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.labels.push(text); } else { return E_FAIL; } } else { return E_FAIL; }
|
||||||
write_tlv_void(result, result_len)
|
write_tlv_void(result, result_len)
|
||||||
}
|
}
|
||||||
M_UI_BUTTON => {
|
M_UI_BUTTON => {
|
||||||
|
eprintln!("[EGUI] M_UI_BUTTON invoked");
|
||||||
// For now: stub, accept and return Void
|
// For now: stub, accept and return Void
|
||||||
if tlv_read_string(args, args_len, 0).is_none() { return E_ARGS; }
|
if tlv_read_string(args, args_len, 0).is_none() { return E_ARGS; }
|
||||||
write_tlv_void(result, result_len)
|
write_tlv_void(result, result_len)
|
||||||
}
|
}
|
||||||
M_POLL_EVENT => {
|
M_POLL_EVENT => {
|
||||||
|
eprintln!("[EGUI] M_POLL_EVENT invoked");
|
||||||
// Stub: no events yet → return empty string "" (Ok)
|
// Stub: no events yet → return empty string "" (Ok)
|
||||||
write_tlv_string("", result, result_len)
|
write_tlv_string("", result, result_len)
|
||||||
}
|
}
|
||||||
M_RUN => {
|
M_RUN => {
|
||||||
// Windows + with-egui: 実ウィンドウを表示
|
eprintln!("[EGUI] M_RUN invoked");
|
||||||
#[cfg(all(windows, feature = "with-egui"))]
|
// with-egui: 実ウィンドウを表示(クロスプラットフォーム)
|
||||||
|
#[cfg(feature = "with-egui")]
|
||||||
{
|
{
|
||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
winrun::run_window(inst.width, inst.height, &inst.title, inst.labels.clone());
|
guirun::run_window(inst.width, inst.height, &inst.title, inst.labels.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,13 +215,14 @@ unsafe fn tlv_read_open_args(args: *const u8, len: usize) -> Option<(i32,i32,Str
|
|||||||
Some((w,h,t))
|
Some((w,h,t))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Windows 実行(with-egui) =====
|
// ===== GUI 実行(with-egui, クロスプラットフォーム) =====
|
||||||
#[cfg(all(windows, feature = "with-egui"))]
|
#[cfg(feature = "with-egui")]
|
||||||
mod winrun {
|
mod guirun {
|
||||||
use super::*;
|
use super::*;
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
pub fn run_window(w: i32, h: i32, title: &str, labels: Vec<String>) {
|
pub fn run_window(w: i32, h: i32, title: &str, labels: Vec<String>) {
|
||||||
|
eprintln!("[EGUI] run_window: w={} h={} title='{}'", w, h, title);
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
.with_inner_size([w.max(100) as f32, h.max(100) as f32])
|
.with_inner_size([w.max(100) as f32, h.max(100) as f32])
|
||||||
@ -233,10 +239,11 @@ mod winrun {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = eframe::run_native(
|
let res = eframe::run_native(
|
||||||
title,
|
title,
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(App { labels })),
|
Box::new(|_cc| Box::new(App { labels })),
|
||||||
);
|
);
|
||||||
|
eprintln!("[EGUI] run_native returned: {:?}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,15 +21,20 @@ pub struct ObjectBuilder {
|
|||||||
pub(crate) block_param_counts: std::collections::HashMap<usize, usize>,
|
pub(crate) block_param_counts: std::collections::HashMap<usize, usize>,
|
||||||
pub stats: (u64,u64,u64,u64,u64),
|
pub stats: (u64,u64,u64,u64,u64),
|
||||||
pub object_bytes: Option<Vec<u8>>,
|
pub object_bytes: Option<Vec<u8>>,
|
||||||
|
// Track rough kinds of values on the stack for bridging (e.g., plugin tagged invoke)
|
||||||
|
value_tags: Vec<ValueTag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum ValueTag { I64, F64, Handle, Unknown }
|
||||||
|
|
||||||
impl ObjectBuilder {
|
impl ObjectBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
use cranelift_codegen::settings;
|
use cranelift_codegen::settings;
|
||||||
let isa = cranelift_native::builder().expect("host ISA").finish(settings::Flags::new(settings::builder())).expect("finish ISA");
|
let isa = cranelift_native::builder().expect("host ISA").finish(settings::Flags::new(settings::builder())).expect("finish ISA");
|
||||||
let obj_builder = cranelift_object::ObjectBuilder::new(isa, "nyash_aot".to_string(), cranelift_module::default_libcall_names()).expect("ObjectBuilder");
|
let obj_builder = cranelift_object::ObjectBuilder::new(isa, "nyash_aot".to_string(), cranelift_module::default_libcall_names()).expect("ObjectBuilder");
|
||||||
let module = cranelift_object::ObjectModule::new(obj_builder);
|
let module = cranelift_object::ObjectModule::new(obj_builder);
|
||||||
Self { module, ctx: cranelift_codegen::Context::new(), fbc: cranelift_frontend::FunctionBuilderContext::new(), current_name: None, entry_block: None, blocks: Vec::new(), current_block_index: None, value_stack: Vec::new(), typed_sig_prepared: false, desired_argc: 0, desired_has_ret: true, desired_ret_is_f64: false, ret_hint_is_b1: false, local_slots: std::collections::HashMap::new(), block_param_counts: std::collections::HashMap::new(), stats: (0,0,0,0,0), object_bytes: None }
|
Self { module, ctx: cranelift_codegen::Context::new(), fbc: cranelift_frontend::FunctionBuilderContext::new(), current_name: None, entry_block: None, blocks: Vec::new(), current_block_index: None, value_stack: Vec::new(), typed_sig_prepared: false, desired_argc: 0, desired_has_ret: true, desired_ret_is_f64: false, ret_hint_is_b1: false, local_slots: std::collections::HashMap::new(), block_param_counts: std::collections::HashMap::new(), stats: (0,0,0,0,0), object_bytes: None, value_tags: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fresh_module() -> cranelift_object::ObjectModule {
|
fn fresh_module() -> cranelift_object::ObjectModule {
|
||||||
@ -59,6 +64,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
use cranelift_frontend::FunctionBuilder;
|
use cranelift_frontend::FunctionBuilder;
|
||||||
self.current_name = Some(name.to_string());
|
self.current_name = Some(name.to_string());
|
||||||
self.value_stack.clear();
|
self.value_stack.clear();
|
||||||
|
self.value_tags.clear();
|
||||||
if !self.typed_sig_prepared {
|
if !self.typed_sig_prepared {
|
||||||
let call_conv = self.module.isa().default_call_conv();
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
let mut sig = Signature::new(call_conv);
|
let mut sig = Signature::new(call_conv);
|
||||||
@ -90,7 +96,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
}
|
}
|
||||||
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) { self.desired_argc = argc; self.desired_has_ret = has_ret; }
|
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) { self.desired_argc = argc; self.desired_has_ret = has_ret; }
|
||||||
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { self.typed_sig_prepared = true; }
|
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { self.typed_sig_prepared = true; }
|
||||||
fn emit_param_i64(&mut self, index: usize) { if let Some(v) = self.entry_param(index) { self.value_stack.push(v); } }
|
fn emit_param_i64(&mut self, index: usize) { if let Some(v) = self.entry_param(index) { self.value_stack.push(v); self.value_tags.push(ValueTag::Unknown); } }
|
||||||
fn emit_const_i64(&mut self, val: i64) {
|
fn emit_const_i64(&mut self, val: i64) {
|
||||||
use cranelift_codegen::ir::types;
|
use cranelift_codegen::ir::types;
|
||||||
use cranelift_frontend::FunctionBuilder;
|
use cranelift_frontend::FunctionBuilder;
|
||||||
@ -99,6 +105,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
let v = fb.ins().iconst(types::I64, val);
|
let v = fb.ins().iconst(types::I64, val);
|
||||||
self.value_stack.push(v);
|
self.value_stack.push(v);
|
||||||
|
self.value_tags.push(ValueTag::I64);
|
||||||
self.stats.0 += 1;
|
self.stats.0 += 1;
|
||||||
}
|
}
|
||||||
fn emit_const_f64(&mut self, val: f64) {
|
fn emit_const_f64(&mut self, val: f64) {
|
||||||
@ -109,6 +116,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
let v = fb.ins().f64const(val);
|
let v = fb.ins().f64const(val);
|
||||||
self.value_stack.push(v);
|
self.value_stack.push(v);
|
||||||
|
self.value_tags.push(ValueTag::F64);
|
||||||
}
|
}
|
||||||
fn emit_binop(&mut self, op: super::BinOpKind) {
|
fn emit_binop(&mut self, op: super::BinOpKind) {
|
||||||
use cranelift_frontend::FunctionBuilder;
|
use cranelift_frontend::FunctionBuilder;
|
||||||
@ -130,6 +138,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
super::BinOpKind::Mod => fb.ins().srem(lhs, rhs),
|
super::BinOpKind::Mod => fb.ins().srem(lhs, rhs),
|
||||||
};
|
};
|
||||||
self.value_stack.push(res);
|
self.value_stack.push(res);
|
||||||
|
self.value_tags.push(ValueTag::I64);
|
||||||
self.stats.1 += 1;
|
self.stats.1 += 1;
|
||||||
}
|
}
|
||||||
fn emit_compare(&mut self, op: super::CmpKind) {
|
fn emit_compare(&mut self, op: super::CmpKind) {
|
||||||
@ -157,6 +166,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
let zero = fb.ins().iconst(types::I64, 0);
|
let zero = fb.ins().iconst(types::I64, 0);
|
||||||
let sel = fb.ins().select(b1, one, zero);
|
let sel = fb.ins().select(b1, one, zero);
|
||||||
self.value_stack.push(sel);
|
self.value_stack.push(sel);
|
||||||
|
self.value_tags.push(ValueTag::I64);
|
||||||
self.stats.2 += 1;
|
self.stats.2 += 1;
|
||||||
}
|
}
|
||||||
fn emit_jump(&mut self) { self.stats.3 += 1; }
|
fn emit_jump(&mut self) { self.stats.3 += 1; }
|
||||||
@ -195,11 +205,12 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
fn store_local_i64(&mut self, index: usize) {
|
fn store_local_i64(&mut self, index: usize) {
|
||||||
use cranelift_frontend::FunctionBuilder;
|
use cranelift_frontend::FunctionBuilder;
|
||||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||||
if let Some(mut v) = self.value_stack.pop() {
|
if let Some(mut v) = self.value_stack.pop() {
|
||||||
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
let _ = self.value_tags.pop();
|
||||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
||||||
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||||
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
// Coerce to i64 if needed
|
// Coerce to i64 if needed
|
||||||
let ty = fb.func.dfg.value_type(v);
|
let ty = fb.func.dfg.value_type(v);
|
||||||
if ty != types::I64 {
|
if ty != types::I64 {
|
||||||
@ -219,6 +230,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
if let Some(&slot) = self.local_slots.get(&index) {
|
if let Some(&slot) = self.local_slots.get(&index) {
|
||||||
let v = fb.ins().stack_load(types::I64, slot, 0);
|
let v = fb.ins().stack_load(types::I64, slot, 0);
|
||||||
self.value_stack.push(v);
|
self.value_stack.push(v);
|
||||||
|
self.value_tags.push(ValueTag::Unknown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn prepare_blocks(&mut self, count: usize) { use cranelift_frontend::FunctionBuilder; if count == 0 { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } }
|
fn prepare_blocks(&mut self, count: usize) { use cranelift_frontend::FunctionBuilder; if count == 0 { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } }
|
||||||
@ -398,7 +410,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
let len_v = fb.ins().iconst(types::I64, bytes.len() as i64);
|
let len_v = fb.ins().iconst(types::I64, bytes.len() as i64);
|
||||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
let call_inst = fb.ins().call(fref, &[lo_v, hi_v, len_v]);
|
let call_inst = fb.ins().call(fref, &[lo_v, hi_v, len_v]);
|
||||||
if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); }
|
if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); self.value_tags.push(ValueTag::Handle); }
|
||||||
}
|
}
|
||||||
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
|
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
|
||||||
use cranelift_frontend::FunctionBuilder;
|
use cranelift_frontend::FunctionBuilder;
|
||||||
@ -409,10 +421,10 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
// Pop else args, then then args (stack topに近い方から)
|
// Pop else args, then then args (stack topに近い方から)
|
||||||
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); } else { else_args.push(fb.ins().iconst(types::I64, 0)); } }
|
for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); let _ = self.value_tags.pop(); } else { else_args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||||
else_args.reverse();
|
else_args.reverse();
|
||||||
let mut then_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
let mut then_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } else { then_args.push(fb.ins().iconst(types::I64, 0)); } }
|
for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); let _ = self.value_tags.pop(); } else { then_args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||||
then_args.reverse();
|
then_args.reverse();
|
||||||
// Cond
|
// Cond
|
||||||
let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) };
|
let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) };
|
||||||
@ -431,10 +443,108 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } else { args.push(fb.ins().iconst(types::I64, 0)); } }
|
for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); let _ = self.value_tags.pop(); } else { args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||||
args.reverse();
|
args.reverse();
|
||||||
for a in args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } }
|
for a in args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } }
|
||||||
fb.ins().jump(self.blocks[target_index], &args);
|
fb.ins().jump(self.blocks[target_index], &args);
|
||||||
self.stats.3 += 1;
|
self.stats.3 += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, argc: usize, has_ret: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
use cranelift_frontend::FunctionBuilder;
|
||||||
|
// We import NyRT tagged invoke entry (by-id). Signature:
|
||||||
|
// nyash_plugin_invoke3_tagged_i64(type_id, method_id, argc, a0, a1, tag1, a2, tag2, a3, tag3, a4, tag4) -> i64
|
||||||
|
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||||
|
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||||
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
|
|
||||||
|
// Pop args in reverse: last pushed is top. Collect up to 4 (excluding recv)
|
||||||
|
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
let mut arg_tags: Vec<ValueTag> = Vec::new();
|
||||||
|
for _ in 0..argc.saturating_sub(1) { // exclude receiver (first param)
|
||||||
|
if let Some(v) = self.value_stack.pop() {
|
||||||
|
arg_vals.push(v);
|
||||||
|
arg_tags.push(self.value_tags.pop().unwrap_or(ValueTag::Unknown));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Receiver
|
||||||
|
let recv = if let Some(v) = self.value_stack.pop() { let _ = self.value_tags.pop(); v } else { fb.ins().iconst(types::I64, 0) };
|
||||||
|
arg_vals.reverse();
|
||||||
|
arg_tags.reverse();
|
||||||
|
let mut tag_i64 = |t: ValueTag| -> i64 { match t { ValueTag::Handle => 8, ValueTag::F64 => 5, ValueTag::I64 => 3, ValueTag::Unknown => 3 } };
|
||||||
|
|
||||||
|
// Build signature and declare import
|
||||||
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
|
for _ in 0..12 { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
|
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
let func_id = self.module.declare_function("nyash_plugin_invoke3_tagged_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke tagged");
|
||||||
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
|
|
||||||
|
// Prepare args array
|
||||||
|
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::with_capacity(12);
|
||||||
|
let to_i64 = |fb: &mut FunctionBuilder, v: cranelift_codegen::ir::Value| {
|
||||||
|
if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v }
|
||||||
|
};
|
||||||
|
|
||||||
|
let t_i64 = |_fb: &mut FunctionBuilder, x: i64| -> cranelift_codegen::ir::Value { _fb.ins().iconst(types::I64, x) };
|
||||||
|
|
||||||
|
// Pass through type_id/method_id from lowering (method_id must match plugin vtable)
|
||||||
|
args.push(t_i64(&mut fb, _type_id as i64)); // type_id (runtime may override with real_type_id)
|
||||||
|
args.push(t_i64(&mut fb, _method_id as i64)); // method_id
|
||||||
|
args.push(t_i64(&mut fb, argc as i64 - 1)); // argc excluding recv
|
||||||
|
args.push(to_i64(&mut fb, recv)); // a0 (recv)
|
||||||
|
|
||||||
|
// a1/tag1, a2/tag2, a3/tag3, a4/tag4
|
||||||
|
for i in 0..4 {
|
||||||
|
if let Some(v) = arg_vals.get(i).copied() {
|
||||||
|
args.push(to_i64(&mut fb, v));
|
||||||
|
let tg = tag_i64(*arg_tags.get(i).unwrap_or(&ValueTag::Unknown));
|
||||||
|
args.push(t_i64(&mut fb, tg));
|
||||||
|
} else {
|
||||||
|
args.push(t_i64(&mut fb, 0));
|
||||||
|
args.push(t_i64(&mut fb, 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let call_inst = fb.ins().call(fref, &args);
|
||||||
|
if has_ret {
|
||||||
|
if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); self.value_tags.push(ValueTag::I64); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_plugin_invoke_by_name(&mut self, _method: &str, argc: usize, has_ret: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
use cranelift_frontend::FunctionBuilder;
|
||||||
|
// Use nyash.plugin.invoke_by_name_i64(recv_h, method_cstr, argc, a1, a2)
|
||||||
|
// Limit: supports up to 2 args beyond receiver.
|
||||||
|
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||||
|
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||||
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
|
|
||||||
|
// Pop args and recv
|
||||||
|
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
for _ in 0..argc.saturating_sub(1) { if let Some(v) = self.value_stack.pop() { arg_vals.push(v); let _ = self.value_tags.pop(); } }
|
||||||
|
let recv = if let Some(v) = self.value_stack.pop() { let _ = self.value_tags.pop(); v } else { fb.ins().iconst(types::I64, 0) };
|
||||||
|
arg_vals.reverse();
|
||||||
|
|
||||||
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
|
for _ in 0..5 { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
|
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
let func_id = self.module.declare_function("nyash.plugin.invoke_by_name_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke by-name");
|
||||||
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
|
|
||||||
|
let to_i64 = |fb: &mut FunctionBuilder, v: cranelift_codegen::ir::Value| { if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v } };
|
||||||
|
let zero = fb.ins().iconst(types::I64, 0);
|
||||||
|
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::with_capacity(5);
|
||||||
|
args.push(to_i64(&mut fb, recv));
|
||||||
|
// method ptr not supported in object builder (no easy CStr symbol payload); pass 0 to let runtime reject if mistakenly used.
|
||||||
|
args.push(zero);
|
||||||
|
args.push(fb.ins().iconst(types::I64, (argc as i64).saturating_sub(1)));
|
||||||
|
args.push(arg_vals.get(0).copied().map(|v| to_i64(&mut fb, v)).unwrap_or(zero));
|
||||||
|
args.push(arg_vals.get(1).copied().map(|v| to_i64(&mut fb, v)).unwrap_or(zero));
|
||||||
|
|
||||||
|
let call_inst = fb.ins().call(fref, &args);
|
||||||
|
if has_ret { if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); self.value_tags.push(ValueTag::I64); } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -225,6 +225,26 @@ impl LowerCore {
|
|||||||
b.store_local_i64(slot);
|
b.store_local_i64(slot);
|
||||||
self.handle_values.insert(*dst);
|
self.handle_values.insert(*dst);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Generic plugin box birth by name via runtime shim: nyash.instance.birth_name_u64x2(lo, hi, len) -> handle
|
||||||
|
// Encode up to 16 bytes of the box type name into two u64 words (little-endian)
|
||||||
|
let name = box_type.as_str();
|
||||||
|
let bytes = name.as_bytes();
|
||||||
|
let take = core::cmp::min(16, bytes.len());
|
||||||
|
let mut lo: u64 = 0; let mut hi: u64 = 0;
|
||||||
|
for i in 0..take.min(8) { lo |= (bytes[i] as u64) << (8 * i as u32); }
|
||||||
|
for i in 8..take { hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); }
|
||||||
|
// Push args and call import
|
||||||
|
b.emit_const_i64(lo as i64);
|
||||||
|
b.emit_const_i64(hi as i64);
|
||||||
|
b.emit_const_i64(bytes.len() as i64);
|
||||||
|
b.emit_host_call("nyash.instance.birth_name_u64x2", 3, true);
|
||||||
|
// Store handle to local slot
|
||||||
|
let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||||
|
b.store_local_i64(slot);
|
||||||
|
self.handle_values.insert(*dst);
|
||||||
|
// Track type for downstream boxcall routing
|
||||||
|
self.box_type_map.insert(*dst, box_type.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
I::Call { dst, func, args, .. } => {
|
I::Call { dst, func, args, .. } => {
|
||||||
|
|||||||
112
tools/windows/build_app_egui_manual.ps1
Normal file
112
tools/windows/build_app_egui_manual.ps1
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<#
|
||||||
|
One‑shot manual build for app_egui.exe (Windows, PowerShell)
|
||||||
|
|
||||||
|
This script replicates the exact manual steps you listed:
|
||||||
|
1) Build Egui plugin (with-egui)
|
||||||
|
2) Build Nyash (with Cranelift AOT toolchain)
|
||||||
|
3) Emit AOT object (main.o) from the Egui Nyash script
|
||||||
|
NOTE: An Egui window will open — close it to continue
|
||||||
|
4) Build the NyRT static runtime library
|
||||||
|
5) Link main.o + NyRT into app_egui.exe using clang (or cc)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
pwsh -File tools/windows/build_app_egui_manual.ps1 \
|
||||||
|
-Input apps/egui-hello-plugin/main.nyash -Out app_egui.exe
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-Input : Path to Nyash Egui script (default: apps/egui-hello-plugin/main.nyash)
|
||||||
|
-Out : Output exe path/name (default: app_egui.exe)
|
||||||
|
-Verbose: Prints extra logs
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Alias('Input')][string]$InputPath = "apps/egui-hello-plugin/main.nyash",
|
||||||
|
[Alias('Out')][string]$OutputExe = "app_egui.exe",
|
||||||
|
[switch]$Verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
function Info($msg) { Write-Host "[manual] $msg" -ForegroundColor Cyan }
|
||||||
|
function Warn($msg) { Write-Host "[manual] $msg" -ForegroundColor Yellow }
|
||||||
|
function Fail($msg) { Write-Host "[manual] ERROR: $msg" -ForegroundColor Red; exit 1 }
|
||||||
|
|
||||||
|
if ($Verbose) { $env:NYASH_CLI_VERBOSE = '1' }
|
||||||
|
|
||||||
|
# Normalize/resolve paths
|
||||||
|
try { $InputPath = (Resolve-Path $InputPath).Path } catch { Fail "Input script not found: $InputPath" }
|
||||||
|
if (-not [System.IO.Path]::IsPathRooted($OutputExe)) { $OutputExe = (Join-Path (Get-Location) $OutputExe) }
|
||||||
|
|
||||||
|
# 1) Egui plugin (with-egui)
|
||||||
|
Info "Building Egui plugin (with-egui)..."
|
||||||
|
Push-Location plugins/nyash-egui-plugin
|
||||||
|
try {
|
||||||
|
cargo build --release --features with-egui | Out-Host
|
||||||
|
} finally { Pop-Location }
|
||||||
|
|
||||||
|
# 2) Nyash core (Cranelift tooling enabled)
|
||||||
|
Info "Building Nyash (cranelift-jit feature for AOT tools)..."
|
||||||
|
cargo build --release --features cranelift-jit | Out-Host
|
||||||
|
|
||||||
|
# 3) Emit main.o via Nyash (AOT object)
|
||||||
|
$env:NYASH_AOT_OBJECT_OUT = if ([string]::IsNullOrWhiteSpace($env:NYASH_AOT_OBJECT_OUT)) { "target/aot_objects" } else { $env:NYASH_AOT_OBJECT_OUT }
|
||||||
|
if (-not (Test-Path $env:NYASH_AOT_OBJECT_OUT)) { [void][System.IO.Directory]::CreateDirectory($env:NYASH_AOT_OBJECT_OUT) }
|
||||||
|
|
||||||
|
# Minimal strictness to keep emission deterministic
|
||||||
|
$env:NYASH_USE_PLUGIN_BUILTINS = '1'
|
||||||
|
$env:NYASH_JIT_EXEC = '1'
|
||||||
|
$env:NYASH_JIT_ONLY = '1'
|
||||||
|
$env:NYASH_JIT_STRICT = '1'
|
||||||
|
$env:NYASH_JIT_ARGS_HANDLE_ONLY = '1'
|
||||||
|
$env:NYASH_JIT_THRESHOLD = '1'
|
||||||
|
|
||||||
|
Info "Emitting main.o (an Egui window will appear — close it to continue)..."
|
||||||
|
& .\target\release\nyash --backend vm $InputPath | Out-Null
|
||||||
|
|
||||||
|
$obj = Join-Path $env:NYASH_AOT_OBJECT_OUT 'main.o'
|
||||||
|
if (-not (Test-Path $obj)) { Fail "object not generated: $obj" }
|
||||||
|
|
||||||
|
# 4) Build NyRT static runtime
|
||||||
|
Info "Building NyRT static runtime..."
|
||||||
|
Push-Location crates/nyrt
|
||||||
|
try {
|
||||||
|
cargo build --release | Out-Host
|
||||||
|
} finally { Pop-Location }
|
||||||
|
|
||||||
|
# 5) Link
|
||||||
|
Info "Linking $OutputExe ..."
|
||||||
|
$libDir = "crates/nyrt/target/release"
|
||||||
|
$libName = ""
|
||||||
|
if (Test-Path (Join-Path $libDir "nyrt.lib")) { $libName = "nyrt.lib" }
|
||||||
|
elseif (Test-Path (Join-Path $libDir "libnyrt.a")) { $libName = "libnyrt.a" }
|
||||||
|
if ($libName -eq "") { Fail "NyRT static library not found in $libDir" }
|
||||||
|
$libPath = Join-Path $libDir $libName
|
||||||
|
|
||||||
|
# Prefer specific LLVM clang if present
|
||||||
|
$clangCandidates = @(
|
||||||
|
"$Env:LLVM_SYS_180_PREFIX\bin\clang.exe",
|
||||||
|
"C:\\LLVM-18\\bin\\clang.exe",
|
||||||
|
(Get-Command clang -ErrorAction SilentlyContinue | ForEach-Object { $_.Source })
|
||||||
|
) | Where-Object { $_ -and (Test-Path $_) }
|
||||||
|
|
||||||
|
if ($clangCandidates.Count -gt 0) {
|
||||||
|
$clang = $clangCandidates[0]
|
||||||
|
Info "Using clang: $clang"
|
||||||
|
& $clang $obj $libPath -o $OutputExe | Out-Host
|
||||||
|
} else {
|
||||||
|
# Fallback: use bash/cc with Linux-like flags, if available (MSYS2/WSL)
|
||||||
|
$bash = Get-Command bash -ErrorAction SilentlyContinue
|
||||||
|
if ($bash) {
|
||||||
|
& bash -lc "cc '$obj' -L '$libDir' -Wl,--whole-archive -lnyrt -Wl,--no-whole-archive -lpthread -ldl -lm -o '$OutputExe'" | Out-Host
|
||||||
|
} else {
|
||||||
|
Fail "Neither clang nor bash/cc found. Install LLVM clang or MSYS2/WSL toolchain."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $OutputExe) {
|
||||||
|
Info "Success. Output: $OutputExe"
|
||||||
|
Write-Host "Run: $OutputExe"
|
||||||
|
} else {
|
||||||
|
Fail "Output exe not found: $OutputExe"
|
||||||
|
}
|
||||||
|
|
||||||
95
tools/windows/build_egui_aot.ps1
Normal file
95
tools/windows/build_egui_aot.ps1
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# NOTE: Save this file with ANSI encoding (no BOM). Use only ASCII characters in this file.
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Alias('Input')][string]$InputPath = "apps/egui-hello-plugin/main.nyash",
|
||||||
|
[Alias('Out')][string]$OutputPath = "app_egui",
|
||||||
|
[switch]$Verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
function Info($msg) { Write-Host "[build] $msg" }
|
||||||
|
function Fail($msg) { Write-Host "[error] $msg"; exit 1 }
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
if ($Verbose) { $env:NYASH_CLI_VERBOSE = "1" }
|
||||||
|
|
||||||
|
# Normalize paths
|
||||||
|
if ([string]::IsNullOrWhiteSpace($InputPath)) { Fail "Input is empty. Example: -Input .\apps\egui-hello-plugin\main.nyash" }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($OutputPath)) { $OutputPath = "app_egui" }
|
||||||
|
try { $InputPath = (Resolve-Path $InputPath).Path } catch { Fail "Input script not found: $InputPath" }
|
||||||
|
if (-not [System.IO.Path]::IsPathRooted($OutputPath)) { $OutputPath = (Join-Path (Get-Location) $OutputPath) }
|
||||||
|
if (-not $OutputPath.ToLower().EndsWith('.exe')) { $OutputExe = "$OutputPath.exe" } else { $OutputExe = $OutputPath }
|
||||||
|
Info "Input=$InputPath"
|
||||||
|
Info "Out=$OutputExe"
|
||||||
|
|
||||||
|
# 1) Build Egui plugin (with-egui)
|
||||||
|
Info "Building Egui plugin (with-egui)..."
|
||||||
|
Push-Location plugins/nyash-egui-plugin
|
||||||
|
try {
|
||||||
|
cargo build --release --features with-egui | Out-Host
|
||||||
|
} catch {
|
||||||
|
Pop-Location
|
||||||
|
Fail "Plugin build failed"
|
||||||
|
}
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
# 2) Build nyash with Cranelift (AOT tools)
|
||||||
|
Info "Building nyash (cranelift-jit feature for AOT tools)..."
|
||||||
|
try {
|
||||||
|
cargo build --release --features cranelift-jit | Out-Host
|
||||||
|
} catch {
|
||||||
|
Fail "nyash build failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3) AOT: emit native exe - MERGED FROM build_aot.ps1
|
||||||
|
Info "Emitting object (.o) via JIT (Strict/No-fallback)..."
|
||||||
|
$host.ui.WriteLine("[build] Heads-up: Running Nyash to emit main.o will open the Egui window. Close the window to continue linking.")
|
||||||
|
$env:NYASH_AOT_OBJECT_OUT = if ([string]::IsNullOrWhiteSpace($env:NYASH_AOT_OBJECT_OUT)) { "target/aot_objects" } else { $env:NYASH_AOT_OBJECT_OUT }
|
||||||
|
$env:NYASH_USE_PLUGIN_BUILTINS = "1"
|
||||||
|
$env:NYASH_JIT_EXEC = "1"
|
||||||
|
$env:NYASH_JIT_ONLY = "1"
|
||||||
|
$env:NYASH_JIT_STRICT = "1"
|
||||||
|
$env:NYASH_JIT_ARGS_HANDLE_ONLY = "1"
|
||||||
|
$env:NYASH_JIT_THRESHOLD = "1"
|
||||||
|
if (-not (Test-Path $env:NYASH_AOT_OBJECT_OUT)) { [void][System.IO.Directory]::CreateDirectory($env:NYASH_AOT_OBJECT_OUT) }
|
||||||
|
& .\target\release\nyash --backend vm $InputPath | Out-Null
|
||||||
|
|
||||||
|
$OBJ = Join-Path $env:NYASH_AOT_OBJECT_OUT "main.o"
|
||||||
|
if (-not (Test-Path $OBJ)) {
|
||||||
|
Fail "object not generated: $OBJ`n hint: ensure main() is lowerable under current Strict JIT coverage"
|
||||||
|
}
|
||||||
|
|
||||||
|
Info "Building libnyrt (static runtime)..."
|
||||||
|
Push-Location crates\nyrt
|
||||||
|
& cargo build --release | Out-Null
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
Info "Linking $OutputExe ..."
|
||||||
|
|
||||||
|
# Try native clang first (LLVM for Windows). On Windows, we avoid -lpthread/-ldl/-lm.
|
||||||
|
$clang = Get-Command clang -ErrorAction SilentlyContinue
|
||||||
|
if ($clang) {
|
||||||
|
$libDir = "crates/nyrt/target/release"
|
||||||
|
$libName = ""
|
||||||
|
if (Test-Path (Join-Path $libDir "nyrt.lib")) { $libName = "nyrt.lib" }
|
||||||
|
elseif (Test-Path (Join-Path $libDir "libnyrt.a")) { $libName = "libnyrt.a" }
|
||||||
|
if ($libName -ne "") {
|
||||||
|
$libPath = Join-Path $libDir $libName
|
||||||
|
$args = @($OBJ, $libPath, "-o", $OutputExe)
|
||||||
|
& clang @args | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $OutputExe)) {
|
||||||
|
$bash = Get-Command bash -ErrorAction SilentlyContinue
|
||||||
|
if ($bash) {
|
||||||
|
& bash -lc "cc target/aot_objects/main.o -L crates/nyrt/target/release -Wl,--whole-archive -lnyrt -Wl,--no-whole-archive -lpthread -ldl -lm -o $OutputExe" | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path "$OutputExe") {
|
||||||
|
Info "Success. Output: $OutputExe"
|
||||||
|
Write-Host "Run: $OutputExe"
|
||||||
|
} else {
|
||||||
|
Fail "Output exe not found: $OutputExe"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user