diff --git a/docs/backend-cranelift-aot-design.md b/docs/backend-cranelift-aot-design.md new file mode 100644 index 00000000..2a7b3ca2 --- /dev/null +++ b/docs/backend-cranelift-aot-design.md @@ -0,0 +1,57 @@ +Cranelift AOT Box: 設計ノートと obj 出力 PoC(Phase 15 準備) + +目的 +- Nyash → MIR → Cranelift AOT(C ABI)→ オブジェクト(.o/.obj)→ リンク → EXE の最小パイプラインを確立する前準備。 +- 本ブランチでは「設計と仕様の確定(ドキュメント化)」のみを行い、実装は別ブランチ `phase-15/self-host-aot-cranelift` で着手する。 + +対象範囲(P0) +- PoC として `ny_main`(i64 → i64 返し)を定義する最小オブジェクトを Cranelift で生成できること。 +- 生成物を NyRT(`crates/nyrt`)と静的リンクして実行可能ファイルを作成できること。 +- 実行結果として `Result: 42` 等の既知の値確認を行うこと。 + +アーキテクチャ概要 +- CraneliftAotBox(本ドキュメントの主題) + - 役割: MIR から Cranelift IR(CLIF)を生成し、`cranelift-object` でオブジェクトを出力する。 + - 出力: ターゲット環境に応じた COFF/ELF/Mach-O(`cranelift-object` の既定に従う)。 + - シグネチャ: PoC は `ny_main: () -> i64`(将来的には引数の受け渡しや NyRT 呼び出しを拡張)。 +- LinkerBox(別タスク、別文書で仕様化) + - 役割: 生成された `.o/.obj` を NyRT(`libnyrt.a`/`nyrt.lib`)とリンクして EXE を作る。 + - Windows は `link.exe`/`lld-link`、Linux は `cc` 経由を想定(詳細は LinkerBox 仕様にて)。 + +ABI / 連携 +- エントリ: `ny_main` を EXE から呼び出す形。NyRT 側が `main()` 内から `ny_main()` を適切に呼び出して結果を表示(または検証)する想定。 +- ランタイム: PoC 段階では NyRT の最低限(起動/終了)に依存。将来的に checkpoint や GC バリアなどの外部関数を `extern "C"` で参照可能にする。 + +PoC 受入基準(P0) +- `.o/.obj` に `ny_main` シンボルが定義されている。 +- `libnyrt.a`/`nyrt.lib` とリンクして実行可能ファイルが作成できる。 +- 実行すると標準出力に既知の値(例: `Result: 42`)が出力される。 + +想定コマンド(リンク例) +- Linux: `cc -o app ny_main.o target/release/libnyrt.a -ldl -lpthread` +- Windows (MSVC): `link ny_main.obj nyrt.lib /OUT:app.exe` +- 実行時設定: 実行ファイルと同じディレクトリに `nyash.toml` を配置することでプラグイン解決を容易にする(NyRT は exe 直下→CWD の順で探索)。 + +CLI/ツール統合(案) +- バックエンドキー: `--backend cranelift-aot` +- PoC フラグ: `--poc-const N`(`ny_main` が `N` を返す単機能) +- 補助スクリプト(設計のみ、本ブランチでは作成しない): + - `tools/aot_smoke_cranelift.sh apps/APP/main.nyash -o app` + - 流れ: Nyash → MIR → CraneliftAotBox → `.o` → LinkerBox/cc → `app` + +ロードマップ +- P0: PoC スタブ `ny_main` 定数返し、リンク/実行確認。 +- P1: 最小 MIR(`const_i64`/`add_i64`/`ret`)のマッピング。 +- P2: NyRT チェックポイント呼び出しなど最小の外部関数連携。 +- P3: Plugin 経由の I/O など実用的な呼び出しの一部導入。 + +既知のリスクと対策 +- プラットフォーム ABI 差異: 既定の呼出規約を使用し、まず Linux で動作確認。 +- オブジェクト形式差: `cranelift-object` の既定に寄り添う。必要に応じてターゲット指定を導入。 +- 重複実装の懸念: 既存のオブジェクトビルダ(JIT/emit系)の再利用・抽象化を検討。 + +実装方針(別ブランチで実施) +- フィーチャ: `cranelift-aot = ["dep:cranelift-object"]` +- モジュール: `src/backend/cranelift/aot_box.rs` を追加し、PoC 用 `compile_stub_ny_main_i64` を提供。 +- CLI 統合: `--backend cranelift-aot` と PoC フラグの導入(PoC 期間は一時的)。 + diff --git a/docs/handoff/phase-15-handoff.md b/docs/handoff/phase-15-handoff.md new file mode 100644 index 00000000..622d0073 --- /dev/null +++ b/docs/handoff/phase-15-handoff.md @@ -0,0 +1,55 @@ +Phase 15 — Self-Hosting (Cranelift AOT) 引き継ぎメモ + +概要(2025-09-05) +- 目的: Nyash → MIR → Cranelift AOT → オブジェクト → リンク → EXE の最小パイプライン確立に向けた準備(設計/仕様/スモーク雛形)。 +- 実装は別ブランチ `phase-15/self-host-aot-cranelift` で着手予定。現状はドキュメントと雛形スクリプトまで整備。 + +このブランチで完了したこと +- Cranelift AOT 設計とインタフェース草案のドキュメント追加: + - docs/backend-cranelift-aot-design.md + - docs/interfaces/cranelift-aot-box.md +- LinkerBox 仕様とAOTスモーク仕様(擬似出力) + - docs/interfaces/linker-box.md + - docs/tests/aot_smoke_cranelift.md +- Phase 15 集約README + - docs/phase-15/README.md +- スモーク雛形(DRYRUN既定。CLIF_SMOKE_RUN=1で実行) + - tools/aot_smoke_cranelift.sh(Unix/WSL) + - tools/aot_smoke_cranelift.ps1(Windows) + +次にやること(別ブランチで実装) +1) ブランチ作成: `git switch -c phase-15/self-host-aot-cranelift` +2) CraneliftAotBox(PoC) + - `src/backend/cranelift/aot_box.rs` を追加 + - `compile_stub_ny_main_i64(val, out_obj)` で `.o/.obj` を出力 + - Cargo feature: `cranelift-aot = ["dep:cranelift-object"]` +3) LinkerBox(Windows優先) + - `.o/.obj` + NyRT(`libnyrt.a`/`nyrt.lib`)で EXE を生成 + - 環境変数: `NYASH_LINKER`/`NYASH_LINK_FLAGS`/`NYASH_LINK_VERBOSE` +4) CLI 統合(PoC) + - `--backend cranelift-aot` と `--poc-const N` +5) スモーク + - apps/ny-hello → emit → link → run → `Result: 42` を確認 + - 既存スクリプト雛形を “実行” モードで動くよう配線 + +運用メモ(Codex 非同期 2本) +- 2本起動: `CODEX_MAX_CONCURRENT=2 CODEX_DEDUP=1 ./tools/codex-keep-two.sh codex "" ""` +- 1本起動: `CODEX_ASYNC_DETACH=1 ./tools/codex-async-notify.sh "" codex` +- ログ: `~/.codex-async-work/logs/` + +スモーク雛形の使い方(DRYRUN) +- Unix/WSL: `./tools/aot_smoke_cranelift.sh release` +- Windows: `pwsh -File tools/aot_smoke_cranelift.ps1 -Mode release` +- 実行モード: `CLIF_SMOKE_RUN=1` を付与(AOT実装が入った後に使用) + +参考リンク +- docs/phase-15/README.md(全体像) +- docs/backend-cranelift-aot-design.md(AOT設計) +- docs/interfaces/cranelift-aot-box.md(CraneliftAotBox API案) +- docs/interfaces/linker-box.md(LinkerBox仕様) +- docs/tests/aot_smoke_cranelift.md(スモーク仕様と擬似出力) + +補足(メモリ/GC) +- P0/P1(定数返し/整数演算)では追加のメモリ系Boxは不要。 +- P2以降で配列/文字列の生成・更新をAOTから行う場合、NyRTに最小のC ABI(roots/barrier/alloc系)を追加予定(docsに設計案を後続追記)。 + diff --git a/docs/interfaces/cranelift-aot-box.md b/docs/interfaces/cranelift-aot-box.md new file mode 100644 index 00000000..18fcebd6 --- /dev/null +++ b/docs/interfaces/cranelift-aot-box.md @@ -0,0 +1,57 @@ +CraneliftAotBox インタフェース草案(Phase 15 準備) + +前提 +- 本ブランチでは「仕様化(ドキュメント)」のみ行い、実装は別ブランチ `phase-15/self-host-aot-cranelift` で行う。 +- Cargo フィーチャ: `cranelift-aot = ["dep:cranelift-object"]` を追加し、同フィーチャ時のみ AOT モジュールを有効化する。 + +モジュール構成(案) +- パス: `src/backend/cranelift/aot_box.rs` +- 依存: `cranelift-object`(オブジェクト出力)、既存のオブジェクトビルダ/ヘルパを再利用可能なら抽象化して流用。 + +公開型(案) +- `pub struct CraneliftAotConfig {` + - `pub opt_level: u8` // 0..3 程度(実装は後続) + - `pub target: Option` // target triple 等(未指定でホスト) +`}` + +- `pub struct CraneliftAotBox {` + - `obj: ` + - `cfg: CraneliftAotConfig` +`}` + +- `#[derive(Debug)] pub enum CraneliftAotError {` + - `Codegen(String)`, `IO(String)` +`}` + +主要メソッド(案) +- `impl CraneliftAotBox {` + - `pub fn new(cfg: CraneliftAotConfig) -> Result` + - `pub fn compile_stub_ny_main_i64(&mut self, val: i64, out_obj: impl AsRef) -> Result<(), CraneliftAotError>` + - 役割: PoC。`ny_main` 関数を定義し、即値 `val` を返すオブジェクトを生成。 + - `pub fn compile_mir_to_obj(&mut self, mir: MirModule, out_obj: impl AsRef) -> Result<(), CraneliftAotError>` + - 役割: P1〜。最小 MIR(`const_i64`/`add_i64`/`ret`)から CLIF を組み立てて出力。 +`}` + +使用例(PoC フロー) +1) NyRT ビルド: `cargo build -p nyrt --release` +2) オブジェクト出力(CLIイメージ): + - `nyash --backend cranelift-aot --poc-const 42 apps/hello/main.nyash -o ny_main.o` +3) リンク: + - Linux: `cc -o app ny_main.o target/release/libnyrt.a -ldl -lpthread` + - Windows: `link ny_main.obj nyrt.lib /OUT:app.exe` +4) 実行: `./app` → `Result: 42` を確認。 + +エラーモデル(案) +- 環境・設定: フィーチャ未有効や未対応ターゲット → 分かりやすいメッセージ。 +- 生成・出力: `CraneliftAotError::Codegen(_)`/`CraneliftAotError::IO(_)` で大別。 + +補助スクリプトの仕様(設計のみ) +- ファイル: `tools/aot_smoke_cranelift.sh` +- 目的: `.o/.obj` を生成→リンク→実行して PoC を自動検証。 +- 主要引数: `apps/APP/main.nyash -o app`、必要に応じ `--const` を透過的に渡す。 + +今後の拡張(非ブロッキング) +- NyRT の外部関数呼び出し(checkpoint など)の導入。 +- MIR 命令カバレッジの拡大、BoxCall/Plugin 経由の I/O。 +- ターゲットトリプルとオブジェクト形式の明示的制御。 + diff --git a/docs/interfaces/linker-box.md b/docs/interfaces/linker-box.md new file mode 100644 index 00000000..736a1775 --- /dev/null +++ b/docs/interfaces/linker-box.md @@ -0,0 +1,48 @@ +LinkerBox 仕様(Phase 15 準備 / Windows優先) + +目的 +- AOT 生成されたオブジェクト(`.o/.obj`)を NyRT とリンクして実行可能ファイルを得る統一レイヤ。 +- 初期実装は「外部リンカー呼び出し」(MSVC `link.exe` または `lld-link`、Unix は `cc`)で動作し、将来的に lld 内蔵連携へ置換可能な設計とする。 + +前提/エントリポイント +- 既定のエントリポイントは NyRT 側の `main()`(`crates/nyrt` に実装)。 +- AOT 側は `ny_main`(想定: `() -> i64`)を定義し、NyRT の `main()` が `ny_main()` を呼び出す。 +- よって通常は `/ENTRY:` 指定は不要(NyRT をリンクしない特殊構成でのみ上書き可能とする)。 + +入力と出力 +- 入力: 一つ以上のオブジェクト(`.obj`/`.o`)、追加ライブラリ群、ライブラリ検索パス。 +- 既定ライブラリ: NyRT(Windows: `nyrt.lib`、Unix: `libnyrt.a`)。 +- 出力: 実行ファイル(Windows: `*.exe`、Unix: 実行ビット付きバイナリ)。 + +環境変数(LinkerBox が解釈) +- `NYASH_LINKER`: 使用リンカーを強制。`link` | `lld-link` | `cc`(未指定は OS/環境から自動推定) +- `NYASH_LINK_FLAGS`: 追加フラグ(空白区切り)。 +- `NYASH_LINK_VERBOSE=1`: 実コマンドラインを表示。 +- `NYASH_LINK_ENTRY=`: エントリポイントを明示的に指定(既定は未指定で NyRT の `main` を使用)。 +- `NYASH_LINK_OUT=`: 出力先を明示的に指定(CLI引数 `-o` が優先)。 + +Windows(MSVC / lld-link) +- 既定探索順: `link.exe` → `lld-link`。 +- 代表フラグ: + - `link.exe`: `/OUT:` `/SUBSYSTEM:CONSOLE`(既定) `/LIBPATH:` `nyrt.lib` 他 + - `lld-link`: `-OUT:` `-SUBSYSTEM:CONSOLE` `-LIBPATH:` `nyrt.lib` +- `PATH`/`LIB`/`LIBPATH` の整合に注意(Developer Command Prompt を推奨)。 + +Unix(参考) +- 代表フラグ: `cc -o -L... -lnyrt -ldl -lpthread` +- 既定のオブジェクト形式/ターゲットはホストに従う。 + +CLI API(想定) +- `nyash --linker link|lld-link|cc --libpath --lib nyrt [--entry nyash_main] -o app ` +- AOT パスでは内部的に LinkerBox を呼び出し、上記環境変数も透過的に反映する。 + +エラー方針 +- ツールチェーン未検出(リンカー不在): わかりやすい対処案を表示(MSVC のセットアップ/lld の導入)。 +- 未解決シンボル: `ny_main`/NyRT 関連の欠落を優先表示。 +- 引数/パスのクォート: 空白を含むパスは安全にクォートして実行。 + +将来拡張 +- 内蔵 lld を採用した一体化(外部プロセス呼び出しからの置換)。 +- ターゲットトリプルの明示指定とクロスリンク(フェーズ後半)。 +- 追加ランタイムやプラグイン静的リンクのオプション化。 + diff --git a/docs/phase-15/README.md b/docs/phase-15/README.md new file mode 100644 index 00000000..ae41d27d --- /dev/null +++ b/docs/phase-15/README.md @@ -0,0 +1,32 @@ +Phase 15 — Self-Hosting (Cranelift AOT) 準備メモ + +目的 +- Nyash → MIR → Cranelift AOT(C ABI)→ オブジェクト → リンク → EXE の最小パイプライン確立。 +- 本ブランチでは「影響小・再現性高い」準備(設計/仕様/スモーク雛形)に限定し、実装は別ブランチで行う。 + +現状ステータス(このブランチ) +- 設計ノート: docs/backend-cranelift-aot-design.md +- インタフェース草案: docs/interfaces/cranelift-aot-box.md +- LinkerBox 仕様: docs/interfaces/linker-box.md +- AOTスモーク仕様(擬似出力): docs/tests/aot_smoke_cranelift.md +- スモーク雛形(DRYRUN 既定): + - tools/aot_smoke_cranelift.sh(Unix/WSL) + - tools/aot_smoke_cranelift.ps1(Windows) + +ハンドオフ +- 引き継ぎの全体像と運用メモは docs/handoff/phase-15-handoff.md を参照。 + +次ブランチで実装する項目(phase-15/self-host-aot-cranelift) +- CraneliftAotBox: `compile_stub_ny_main_i64` → `.o/.obj` を出力。 +- LinkerBox: `.o/.obj` + NyRT(libnyrt)で EXE にリンク(Windows優先)。 +- CLI統合: `--backend cranelift-aot` と PoC フラグ(`--poc-const`)。 +- スモーク実行: apps/ny-hello → EXE 生成・起動確認。 + +合否基準(P0) +- `ny_main` を定義するオブジェクトを生成できる。 +- NyRT とリンクして EXE を生成できる。 +- 実行し、既知の値(例: `Result: 42`)を出力。 + +補足 +- Windowsを先行サポートし、Linux/macOS は後続対応。 +- 実出力やビルドログは `tools/codex-async-notify.sh` のログ参照運用を継続。 diff --git a/docs/tests/aot_smoke_cranelift.md b/docs/tests/aot_smoke_cranelift.md new file mode 100644 index 00000000..327fe3fa --- /dev/null +++ b/docs/tests/aot_smoke_cranelift.md @@ -0,0 +1,75 @@ +# Cranelift AOT Smoke (Windows‑first) + +Purpose +- Validate the Cranelift‑based AOT pipeline end‑to‑end: + 1) Build `nyash` with `cranelift-jit` feature. + 2) Emit an object via `NYASH_AOT_OBJECT_OUT` while running `--backend cranelift`. + 3) Link the object with NyRT into a runnable binary (via LinkerBox or helper scripts). + 4) Run the binary and assert output. + +Prerequisites +- Build flags: `cargo build --release --features cranelift-jit` +- Windows: + - Prefer MSVC `link.exe` (Developer Command Prompt or properly set env). + - Fallback: `lld-link` in `PATH`. + - PowerShell available for `tools/aot_smoke_cranelift.ps1`. +- Unix (optional): system linker (`ld`), or `lld`/`mold`, and `tools/aot_smoke_cranelift.sh`. + +Environment toggles +- `NYASH_CLIF_ARRAY_SMOKE=1`: run array smoke (simple Result check). +- `NYASH_CLIF_ARRAY_RET_SMOKE=1`: run “return value” array smoke. +- `NYASH_CLIF_ECHO_SMOKE=1`: run echo smoke (stdin → stdout). +- `NYASH_CLIF_VINVOKE_SMOKE=1`: run variable‑length invoke smoke (plugins required). +- `NYASH_CLIF_VINVOKE_RET_SMOKE=1`: run vinvoke return/size smokes (plugins required). +- `NYASH_DISABLE_PLUGINS=1`: disable plugin‑dependent smokes. +- `NYASH_LINK_VERBOSE=1`: print final link command. + +Pseudo run +- Script: `tools/aot_smoke_cranelift.sh` / `tools/aot_smoke_cranelift.ps1` +- Typical invocation: `./tools/aot_smoke_cranelift.sh release` + +Pseudo output (example) +``` +[clif-aot-smoke] building nyash (release, feature=cranelift-jit)... +[clif-aot-smoke] emitting object via --backend cranelift ... +[clif-aot-smoke] OK: object generated: /ABS/path/target/aot_objects/core_smoke.obj (1536 bytes) + +[clif-aot-smoke][win] linking app_clif.exe using link.exe +[clif-aot-smoke][win] entry=nyash_main subsystem=CONSOLE runtime=nyrt.lib +[clif-aot-smoke] running app_clif.exe ... +[clif-aot-smoke] output: Result: 3 +[clif-aot-smoke] OK: core smoke passed + +[clif-aot-smoke] skipping array smoke (set NYASH_CLIF_ARRAY_SMOKE=1 to enable) +[clif-aot-smoke] skipping echo smoke (set NYASH_CLIF_ECHO_SMOKE=1 to enable) +[clif-aot-smoke] skipping vinvoke smokes (set NYASH_CLIF_VINVOKE_SMOKE=1 / NYASH_CLIF_VINVOKE_RET_SMOKE=1) +``` + +What the script does (intended) +- Build: + - `cargo build --release --features cranelift-jit` +- Emit object: + - Ensure stable output dir: `mkdir -p target/aot_objects` + - `NYASH_AOT_OBJECT_OUT="$PWD/target/aot_objects/core_smoke.obj" ./target/release/nyash --backend cranelift apps/hello/main.nyash > /dev/null || true` + - Validate file exists and non‑zero size. +- Link: + - Windows: PowerShell `tools/aot_smoke_cranelift.ps1 -Mode release` + - Unix: `tools/aot_smoke_cranelift.sh release` +- Run and verify: + - `./app_clif[.exe]` → expect a line including `Result:`. + +Windows specifics +- Prefer MSVC `link.exe`; auto‑fallback to `lld-link` if present. +- If neither available, fail with a helpful message to open a Developer Command Prompt or install LLVM lld. +- Use `.obj` extension for emitted object; still accept `.o` if emitted by a GNU toolchain. + +Exit codes +- 0: all enabled smokes passed +- 1: object missing/empty, or unexpected program output +- 2: toolchain missing (no Cranelift build or no linker) + +Future alignment with LinkerBox +- This smoke is the acceptance test for LinkerBox’s AOT path on Cranelift: + - Same entrypoint (`nyash_main`), runtime linkage (`nyrt.lib`/`libnyrt.a`), and CLI env (`NYASH_LINKER`, `NYASH_LINK_FLAGS`, `NYASH_LINK_VERBOSE`). + - When LinkerBox becomes default, keep CLI stable and swap implementation behind it. + diff --git a/tools/aot_smoke_cranelift.ps1 b/tools/aot_smoke_cranelift.ps1 new file mode 100644 index 00000000..f437263c --- /dev/null +++ b/tools/aot_smoke_cranelift.ps1 @@ -0,0 +1,88 @@ +<# + AOT smoke (Cranelift) — DRYRUN skeleton (Windows-first) + Usage: + pwsh -File tools/aot_smoke_cranelift.ps1 [-Mode release|debug] + Env: + CLIF_SMOKE_RUN=1 # actually execute steps (default: dry-run only) + NYASH_LINK_VERBOSE=1 # echo link commands (when run) + NYASH_DISABLE_PLUGINS=1 # plugin-dependent smokes off + Notes: + - This script mirrors docs/tests/aot_smoke_cranelift.md pseudo flow. + - PoC: emits commands; real execution requires Cranelift AOT path to be implemented. +#> + +param( + [ValidateSet('release','debug')] + [string]$Mode = 'release' +) + +$Run = [int]([Environment]::GetEnvironmentVariable('CLIF_SMOKE_RUN') ?? '0') + +function Banner($msg) { Write-Host "`n[clif-aot-smoke] $msg" } +function Info($msg) { Write-Host "[clif-aot-smoke] $msg" } + +$Root = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$Target = Join-Path $Root 'target' +$ObjDir = Join-Path $Target 'aot_objects' +New-Item -ItemType Directory -Force -Path $ObjDir | Out-Null +$ObjOut = Join-Path $ObjDir 'core_smoke.obj' +$NyashBin = Join-Path (Join-Path $Target $Mode) 'nyash.exe' +$ExeOut = Join-Path $Target 'app_clif.exe' + +Banner "Cranelift AOT Smoke (mode=$Mode, dry-run=$([bool](-not ($Run -eq 1))))" + +# 1) Build nyash with cranelift +Banner "building nyash (features=cranelift-jit)" +if ($Run -eq 1) { + & cargo build --% --$Mode --features cranelift-jit +} else { + Info "DRYRUN: cargo build --$Mode --features cranelift-jit" +} + +# 2) Emit object via backend=cranelift (PoC path) +Banner "emitting object via --backend cranelift (PoC)" +if ($Run -eq 1) { + if (-not (Test-Path $NyashBin)) { throw "nyash not found: $NyashBin" } + $env:NYASH_AOT_OBJECT_OUT = $ObjOut + & $NyashBin --backend cranelift apps/hello/main.nyash | Out-Null + if (-not (Test-Path $ObjOut)) { throw "object not generated: $ObjOut" } + $size = (Get-Item $ObjOut).Length + Info "OK: object generated: $ObjOut ($size bytes)" +} else { + Info "DRYRUN: NYASH_AOT_OBJECT_OUT=$ObjOut $NyashBin --backend cranelift apps/hello/main.nyash" + New-Item -ItemType File -Force -Path $ObjOut | Out-Null +} + +# 3) Link (Windows-first) +Banner "linking app (Windows-first)" +if ($Run -eq 1) { + $link = Get-Command link -ErrorAction SilentlyContinue + $lld = Get-Command lld-link -ErrorAction SilentlyContinue + if ($link) { + Info "using MSVC link.exe" + & link /OUT:$ExeOut $ObjOut nyrt.lib + } elseif ($lld) { + Info "using lld-link" + & lld-link -OUT:$ExeOut $ObjOut nyrt.lib + } else { + throw "no Windows linker found (link.exe/lld-link)" + } +} else { + Info "DRYRUN: link /OUT:$ExeOut $ObjOut nyrt.lib (or lld-link)" +} + +# 4) Run and verify +Banner "run and verify output" +if ($Run -eq 1) { + if (-not (Test-Path $ExeOut)) { throw "no output binary: $ExeOut" } + $out = & $ExeOut 2>&1 + $out | Write-Host + if ($out -notmatch 'Result:') { throw "unexpected output" } + Info "OK: smoke passed" +} else { + Info "DRYRUN: $ExeOut → expect a line including: 'Result:'" + Info "DRYRUN complete" +} + +exit 0 + diff --git a/tools/aot_smoke_cranelift.sh b/tools/aot_smoke_cranelift.sh new file mode 100644 index 00000000..55053ed1 --- /dev/null +++ b/tools/aot_smoke_cranelift.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# AOT smoke (Cranelift) — DRYRUN skeleton (Windows-first) +# +# Usage: +# ./tools/aot_smoke_cranelift.sh [release|debug] +# Env: +# CLIF_SMOKE_RUN=1 # actually execute steps (default: dry-run only) +# CODEX_NOTIFY_TAIL=100 # for CI/logging callers (optional) +# NYASH_LINK_VERBOSE=1 # echo link commands (when run) +# NYASH_DISABLE_PLUGINS=1 # plugin-dependent smokes off +# NYASH_CLIF_* # feature toggles (see docs/tests/aot_smoke_cranelift.md) + +set -euo pipefail + +MODE=${1:-release} +case "$MODE" in + release|debug) : ;; + *) echo "Usage: $0 [release|debug]" >&2; exit 2;; +esac + +RUN=${CLIF_SMOKE_RUN:-0} +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +TARGET_DIR="$ROOT_DIR/target" +OBJ_DIR="$TARGET_DIR/aot_objects" +EXE_WIN="$TARGET_DIR/app_clif.exe" +EXE_NIX="$TARGET_DIR/app_clif" + +banner() { printf '\n[clif-aot-smoke] %s\n' "$*"; } +info() { printf '[clif-aot-smoke] %s\n' "$*"; } +skip() { printf '[clif-aot-smoke] skipping %s (enable env to run)\n' "$*"; } + +banner "Cranelift AOT Smoke (mode=$MODE, dry-run=$([ "$RUN" = 1 ] && echo off || echo on))" + +mkdir -p "$OBJ_DIR" +OBJ_OUT="$OBJ_DIR/core_smoke.$([ "$(uname -s)" = "Windows_NT" ] && echo obj || echo o)" +NYASH_BIN="$ROOT_DIR/target/$MODE/nyash" + +# 1) Build nyash with cranelift +banner "building nyash (features=cranelift-jit)" +if [ "$RUN" = 1 ]; then + cargo build --$MODE --features cranelift-jit +else + info "DRYRUN: cargo build --$MODE --features cranelift-jit" +fi + +# 2) Emit object via backend=cranelift (PoC path; may be stub until implemented) +banner "emitting object via --backend cranelift (PoC)" +if [ "$RUN" = 1 ]; then + if [ ! -x "$NYASH_BIN" ]; then + echo "nyash binary not found: $NYASH_BIN" >&2; exit 2 + fi + NYASH_AOT_OBJECT_OUT="$OBJ_OUT" "$NYASH_BIN" --backend cranelift apps/hello/main.nyash || true + if [ ! -s "$OBJ_OUT" ]; then + echo "object not generated (expected PoC path)." >&2; exit 1 + fi + info "OK: object generated: $OBJ_OUT ($(stat -c%s "$OBJ_OUT" 2>/dev/null || wc -c <"$OBJ_OUT")) bytes)" +else + info "DRYRUN: NYASH_AOT_OBJECT_OUT=\"$OBJ_OUT\" $NYASH_BIN --backend cranelift apps/hello/main.nyash" + info "DRYRUN: touch $OBJ_OUT (pretend non-empty)" +fi + +# 3) Link (Windows-first). In DRYRUN, just print the command. +banner "linking app (Windows-first)" +if [ "$RUN" = 1 ]; then + case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*|Windows_NT) + if command -v link >/dev/null 2>&1; then + info "using MSVC link.exe" + link /OUT:"$EXE_WIN" "$OBJ_OUT" nyrt.lib || { echo "link failed" >&2; exit 1; } + OUT_BIN="$EXE_WIN" + elif command -v lld-link >/dev/null 2>&1; then + info "using lld-link" + lld-link -OUT:"$EXE_WIN" "$OBJ_OUT" nyrt.lib || { echo "lld-link failed" >&2; exit 1; } + OUT_BIN="$EXE_WIN" + else + echo "no Windows linker found (link.exe/lld-link)" >&2; exit 2 + fi + ;; + *) + if command -v cc >/dev/null 2>&1; then + cc -o "$EXE_NIX" "$OBJ_OUT" "$TARGET_DIR/release/libnyrt.a" -ldl -lpthread || { echo "cc link failed" >&2; exit 1; } + OUT_BIN="$EXE_NIX" + else + echo "no cc found for Unix link" >&2; exit 2 + fi + ;; + esac +else + case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*|Windows_NT) + info "DRYRUN: link /OUT:$EXE_WIN $OBJ_OUT nyrt.lib (or lld-link)" + ;; + *) + info "DRYRUN: cc -o $EXE_NIX $OBJ_OUT target/release/libnyrt.a -ldl -lpthread" + ;; + esac +fi + +# 4) Run and verify +banner "run and verify output" +if [ "$RUN" = 1 ]; then + if [ -z "${OUT_BIN:-}" ] || [ ! -x "$OUT_BIN" ]; then + echo "no output binary to run" >&2; exit 1 + fi + set +e + OUTPUT="$($OUT_BIN 2>&1)"; RC=$? + set -e + echo "$OUTPUT" + echo "$OUTPUT" | grep -q "Result:" || { echo "unexpected output" >&2; exit 1; } + info "OK: smoke passed" +else + info "DRYRUN: ./app_clif[.exe] → expect a line including: 'Result: 3' or 'Result: 42'" + info "DRYRUN complete" +fi + +exit 0 +