phase15: add Cranelift AOT design, LinkerBox spec, and smoke skeleton docs (handoff)

This commit is contained in:
Moe Charm
2025-09-05 05:49:26 +09:00
parent 19f775c34d
commit e05a385524
8 changed files with 529 additions and 0 deletions

View File

@ -0,0 +1,57 @@
Cranelift AOT Box: 設計ノートと obj 出力 PoCPhase 15 準備)
目的
- Nyash → MIR → Cranelift AOTC 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 IRCLIFを生成し、`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 期間は一時的)。

View File

@ -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.shUnix/WSL
- tools/aot_smoke_cranelift.ps1Windows
次にやること(別ブランチで実装)
1) ブランチ作成: `git switch -c phase-15/self-host-aot-cranelift`
2) CraneliftAotBoxPoC
- `src/backend/cranelift/aot_box.rs` を追加
- `compile_stub_ny_main_i64(val, out_obj)``.o/.obj` を出力
- Cargo feature: `cranelift-aot = ["dep:cranelift-object"]`
3) LinkerBoxWindows優先
- `.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 "<Task A>" "<Task B>"`
- 1本起動: `CODEX_ASYNC_DETACH=1 ./tools/codex-async-notify.sh "<task>" 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.mdAOT設計
- docs/interfaces/cranelift-aot-box.mdCraneliftAotBox API案
- docs/interfaces/linker-box.mdLinkerBox仕様
- docs/tests/aot_smoke_cranelift.mdスモーク仕様と擬似出力
補足(メモリ/GC
- P0/P1定数返し/整数演算では追加のメモリ系Boxは不要。
- P2以降で配列/文字列の生成・更新をAOTから行う場合、NyRTに最小のC ABIroots/barrier/alloc系を追加予定docsに設計案を後続追記

View File

@ -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<String>` // target triple 等(未指定でホスト)
`}`
- `pub struct CraneliftAotBox {`
- `obj: <object builder>`
- `cfg: CraneliftAotConfig`
`}`
- `#[derive(Debug)] pub enum CraneliftAotError {`
- `Codegen(String)`, `IO(String)`
`}`
主要メソッド(案)
- `impl CraneliftAotBox {`
- `pub fn new(cfg: CraneliftAotConfig) -> Result<Self, CraneliftAotError>`
- `pub fn compile_stub_ny_main_i64(&mut self, val: i64, out_obj: impl AsRef<Path>) -> Result<(), CraneliftAotError>`
- 役割: PoC。`ny_main` 関数を定義し、即値 `val` を返すオブジェクトを生成。
- `pub fn compile_mir_to_obj(&mut self, mir: MirModule, out_obj: impl AsRef<Path>) -> 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。
- ターゲットトリプルとオブジェクト形式の明示的制御。

View File

@ -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`)、追加ライブラリ群、ライブラリ検索パス。
- 既定ライブラリ: NyRTWindows: `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=<symbol>`: エントリポイントを明示的に指定(既定は未指定で NyRT の `main` を使用)。
- `NYASH_LINK_OUT=<path>`: 出力先を明示的に指定CLI引数 `-o` が優先)。
WindowsMSVC / lld-link
- 既定探索順: `link.exe``lld-link`
- 代表フラグ:
- `link.exe`: `/OUT:<exe>` `/SUBSYSTEM:CONSOLE`(既定) `/LIBPATH:<dir>` `nyrt.lib`
- `lld-link`: `-OUT:<exe>` `-SUBSYSTEM:CONSOLE` `-LIBPATH:<dir>` `nyrt.lib`
- `PATH`/`LIB`/`LIBPATH` の整合に注意Developer Command Prompt を推奨)。
Unix参考
- 代表フラグ: `cc -o <exe> <objs...> <lib paths> -L... -lnyrt -ldl -lpthread`
- 既定のオブジェクト形式/ターゲットはホストに従う。
CLI API想定
- `nyash --linker link|lld-link|cc --libpath <dir> --lib nyrt [--entry nyash_main] -o app <objs...>`
- AOT パスでは内部的に LinkerBox を呼び出し、上記環境変数も透過的に反映する。
エラー方針
- ツールチェーン未検出(リンカー不在): わかりやすい対処案を表示MSVC のセットアップlld の導入)。
- 未解決シンボル: `ny_main`/NyRT 関連の欠落を優先表示。
- 引数/パスのクォート: 空白を含むパスは安全にクォートして実行。
将来拡張
- 内蔵 lld を採用した一体化(外部プロセス呼び出しからの置換)。
- ターゲットトリプルの明示指定とクロスリンク(フェーズ後半)。
- 追加ランタイムやプラグイン静的リンクのオプション化。

32
docs/phase-15/README.md Normal file
View File

@ -0,0 +1,32 @@
Phase 15 — Self-Hosting (Cranelift AOT) 準備メモ
目的
- Nyash → MIR → Cranelift AOTC 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.shUnix/WSL
- tools/aot_smoke_cranelift.ps1Windows
ハンドオフ
- 引き継ぎの全体像と運用メモは docs/handoff/phase-15-handoff.md を参照。
次ブランチで実装する項目phase-15/self-host-aot-cranelift
- CraneliftAotBox: `compile_stub_ny_main_i64``.o/.obj` を出力。
- LinkerBox: `.o/.obj` + NyRTlibnyrtで 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` のログ参照運用を継続。

View File

@ -0,0 +1,75 @@
# Cranelift AOT Smoke (Windowsfirst)
Purpose
- Validate the Craneliftbased AOT pipeline endtoend:
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 variablelength invoke smoke (plugins required).
- `NYASH_CLIF_VINVOKE_RET_SMOKE=1`: run vinvoke return/size smokes (plugins required).
- `NYASH_DISABLE_PLUGINS=1`: disable plugindependent 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 nonzero 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`; autofallback 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 LinkerBoxs 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.

View File

@ -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

View File

@ -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