feat(phase21.5): MirBuilder optimization prep + crate EXE infrastructure

Phase 21.5 optimization readiness - C-level performance target:
- MirBuilder: JsonFrag purify toggle (HAKO_MIR_BUILDER_JSONFRAG_PURIFY=1)
- Normalizer: extended f64 canonicalization + dedupe improvements
- loop_opts_adapter: JsonFrag path refinement for crate EXE compatibility

Infrastructure improvements:
- provider_registry: add diagnostics + ring-1 providers (array/console/map/path)
- mir_interpreter: add normalization/purify feature gates
- tools/selfhost_exe_stageb.sh: new end-to-end Stage-B→crate EXE pipeline
- tools/perf/microbench.sh: performance measurement tooling

Smoke tests (phase2100):
- Extend timeout 15s→120s for heavy crate EXE builds
- Add stageb_loop_jsonfrag_crate_exe_canary_vm.sh (target test)
- Add s3_backend_selector_crate_exe_vm_parity_return42_canary_vm.sh

Documentation:
- ENV_VARS.md: add Phase 21.5 optimization toggles
- README updates: clarify crate backend strategy
- phase215-optimization.md: new optimization roadmap

This commit sets the stage for Phase 21.5 critical optimization:
achieving C-level performance to decide hakorune's future viability.
This commit is contained in:
nyash-codex
2025-11-11 02:07:12 +09:00
parent ece91306b7
commit 07a254fc0d
49 changed files with 905 additions and 167 deletions

View File

@ -8,13 +8,22 @@ Todays update (structure-first)
- Added MirBuilderMin (bringup minimal builder): `hako.mir.builder.min` - Added MirBuilderMin (bringup minimal builder): `hako.mir.builder.min`
- Purpose: tiny using set to avoid heavy prelude in this host; emits `[mirbuilder/min:*]` tags. - Purpose: tiny using set to avoid heavy prelude in this host; emits `[mirbuilder/min:*]` tags.
- New canaries (PASS): builder_min_method_arraymap_{get,push,set,len}_canary_vm.sh - New canaries (PASS): builder_min_method_arraymap_{get,push,set,len}_canary_vm.sh
- Added direct-lower canary (PASS): registry_optin_method_arraymap_direct_canary_vm.sh - Added direct-lower canary (PASS): registry_optin_method_arraymap_direct_canary_vm.sh
- Phase2034 canaries: migrated several rcbased checks to content checks - Phase2034 canaries: migrated several rcbased checks to content checks
- If family: varint/varvar/nested/then-follow → now print MIR and assert '"op":"compare"'+'"op":"branch"' - If family: varint/varvar/nested/then-follow → now print MIR and assert '"op":"compare"'+'"op":"branch"'
- Return family: var_local/bool/float/string/binop_varint/logical → now print MIR and assert minimal tokens - Return family: var_local/bool/float/string/binop_varint/logical → now print MIR and assert minimal tokens
- Removed nested command substitution in `mirbuilder_canary_vm.sh`; switched to content assertion - Removed nested command substitution in `mirbuilder_canary_vm.sh`; switched to content assertion
- Registry(get) Full path: exercised fast path (JsonFragonly) via `HAKO_MIR_BUILDER_REGISTRY_ONLY=return.method.arraymap` and pinned canary to registry tag only - Registry(get) Full path: exercised fast path (JsonFragonly) via `HAKO_MIR_BUILDER_REGISTRY_ONLY=return.method.arraymap` and pinned canary to registry tag only
Additional updates (dev-only, behavior unchanged by default)
- VM counters wired: `inst_count`nonphi, `compare_count`Compare, `branch_count`Branch
- Print with `NYASH_VM_STATS=1` as `[vm/stats] inst=… compare=… branch=…`
- Microbench harness: fixed ratio print noiseheredoc→oneliner
- EXE canaries: crate backend bringup hardened
- Added `enable_exe_dev_env` helper and applied to representative EXE tests
- Enabled `NYASH_LLVM_VERIFY=1`/`NYASH_LLVM_VERIFY_IR=1` for deterministic cases
- Promoted `return42` canary to FAIL on mismatch (was SKIP)
Next — Phase 21.5 (Optimization, AOT-first) Next — Phase 21.5 (Optimization, AOT-first)
- Baseline targets: Box create/destroy, method-call-onlysmall - Baseline targets: Box create/destroy, method-call-onlysmall
- Harness: `tools/perf/run_all_21_5.sh`, `tools/perf/bench_compare_c_vs_hako.sh`, `tools/perf/record_baselines.sh` - Harness: `tools/perf/run_all_21_5.sh`, `tools/perf/bench_compare_c_vs_hako.sh`, `tools/perf/record_baselines.sh`
@ -37,6 +46,56 @@ Notes
- Do not broaden default behavior; optimization work remains optin with clear flags. - Do not broaden default behavior; optimization work remains optin with clear flags.
- Avoid Rust fallback routes during bringup (FailFast by default); canaries may set localized `NYASH_FAIL_FAST=0` only when needed. - Avoid Rust fallback routes during bringup (FailFast by default); canaries may set localized `NYASH_FAIL_FAST=0` only when needed.
---
StageB → MirBuilder → nyllvmccrate EXE— PreOptimization Goal
- Goal (pre21.5 optimization): build EXE via crate backend (nyllvmc) from StageB Program(JSON) → MirBuilder MIR(JSON) before focusing on perf. Make EXE selfhosting reliable on this host.
- Why now
- VM selfhost is green. EXE canaries (return/compare/binop/phi/externcall) are green.
- Loop/strlen general cases fail in crate EXE due to builder output mixing `newbox/MapBox` (loop) and missing FAST path (strlen). Fix these first to unlock fair “executeonly” microbench.
- Plan (small, reversible, defaultOFF)
1) Loop JSONFrag purification (no MapBox/newbox)
- Ensure `HAKO_MIR_BUILDER_LOOP_JSONFRAG=1` route emits pure controlflow MIR only (const/compare/branch/phi/ret).
- Acceptance: MIR(JSON) for minimal loop contains no `newbox|MapBox`; crate EXE build PASS; add canary to assert absence and EXE RC parity.
2) strlen FAST path (optin)
- nyllvmc lowering (FAST=1): `externcall nyrt_string_length(i8*, i64)` (returns i64; no boxing).
- Add `nyrt_string_length` to `crates/nyash_kernel` (minimal impl; byte/char mode param reserved).
- Acceptance: FAST=1 で crate EXE RC=5"nyash".length; FAST=0 挙動不変。
3) Selfhost EXE wrapper (dev helper)
- Script (plan): `tools/selfhost_exe_stageb.sh`
- StageB Program(JSON) emit → MirBuilder to MIR(JSON) via `tools/hakorune_emit_mir.sh`
- Build EXE via `tools/ny_mir_builder.sh --emit exe` (force `NYASH_LLVM_BACKEND=crate`)
- Optional: run + RC parity check vs VM
4) Canaries / Runner
- Loop JSONFrag noMapBox check (FAIL 基準)
- strlen FAST EXE RC checkFAST=1 のみタグ観測既定OFFで静音
- EXE parity: minimal StageB sample VM↔EXE RC 一致
5) Acceptance & Docs
- quick 代表EXE混在`--timeout 120` 推奨で緑維持(既定は変更しない)
- CURRENT_TASK/README に切替手順とフラグ記載(ロールバック容易)
- Toggles / Scripts
- `HAKO_MIR_BUILDER_LOOP_JSONFRAG=1`Loop を JSONFrag 直組立に固定)
- `HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1`(任意)
- `NYASH_LLVM_BACKEND=crate` / `NYASH_NY_LLVM_COMPILER=target/release/ny-llvmc` / `NYASH_EMIT_EXE_NYRT=target/release`
- Helper: `tools/hakorune_emit_mir.sh`StageB→MIR`tools/ny_mir_builder.sh --emit exe`crate で obj/exe
- Done (recent)
- EXE canaries hardenedenable_exe_dev_env 適用・検証ON・代表は FAIL 基準へ昇格)
- VM runtime counters`NYASH_VM_STATS=1`
- microbench `--exe` 実装ビルド1回→EXEをRUNS回実行。現状、loop/strlen の一般形は crate 未対応で EXE 失敗 → 上記 1)/2) で解消予定。
- Next (actionable)
- Implement loop JSONFrag purification (no MapBox/newbox) and add canary
- Add strlen FAST lowering + `nyrt_string_length` (kernel) + EXE canary
- Wire `tools/selfhost_exe_stageb.sh` and record one EXE parity check
- Then resume 21.5 optimization and run microbench with `--exe`executeonly timing
MirBuilder/JsonFrag note
- JsonFrag path is for structure observation only (minimal MIR). Semantics remain prioritized by the default path. Keep it default OFF and taggated. Builder toggles are centralized in `hako.mir.builder.internal.builder_config` to reduce scattered `env.get` calls in lowers.
Toggles (dev) Toggles (dev)
- `NYASH_VM_FAST=1`VM micro fast paths: new StringBox, String.length/size - `NYASH_VM_FAST=1`VM micro fast paths: new StringBox, String.length/size
- `NYASH_LLVM_FAST=1`ny-llvmc AOT lowering fast path; default OFF - `NYASH_LLVM_FAST=1`ny-llvmc AOT lowering fast path; default OFF

View File

@ -13,6 +13,9 @@
--- ---
アーキテクチャノート
- 実行リングring0/ring1/ring2とプロバイダ選択ポリシー: `docs/architecture/RINGS.md`
開発者向けクイックスタート: `docs/guides/getting-started.md` 開発者向けクイックスタート: `docs/guides/getting-started.md`
ユーザーマクロPhase 2: `docs/guides/user-macros.md` ユーザーマクロPhase 2: `docs/guides/user-macros.md`
AST JSON v0マクロ/ブリッジ): `docs/reference/ir/ast-json-v0.md` AST JSON v0マクロ/ブリッジ): `docs/reference/ir/ast-json-v0.md`

View File

@ -27,6 +27,9 @@ Notes
--- ---
Architecture notes
- Runtime rings (ring0/ring1/ring2) and provider policy: see `docs/architecture/RINGS.md`.
Execution Status (Feature Additions Pause) Execution Status (Feature Additions Pause)
- Active - Active
- `--backend llvm` (Python/llvmlite harness; AOT object emit) - `--backend llvm` (Python/llvmlite harness; AOT object emit)

View File

@ -206,3 +206,17 @@ VM/AOT fast-path toggles (bench/dev only)
- Enables AOT lowering tweaks in ny-llvmc (opt-in, benches only): - Enables AOT lowering tweaks in ny-llvmc (opt-in, benches only):
- StringBox.length/size lowered to extern helper returning i64 (no boxing) - StringBox.length/size lowered to extern helper returning i64 (no boxing)
- Default OFF. Guarded in ny-llvmc; legacy lowering remains default. - Default OFF. Guarded in ny-llvmc; legacy lowering remains default.
MirBuilder toggles (trace and JsonFrag)
- `HAKO_MIR_BUILDER_TRACE=0|1` (default: 0)
- When 1, emits concise builder tags like `[mirbuilder/internal/*]` from Hako lowers. Errors remain visible regardless; this flag controls informational tags.
- `HAKO_MIR_BUILDER_LOOP_JSONFRAG=0|1` (default: 0)
- Enable JsonFrag minimal MIR assembly in loop adapters/lowers (structure observation only).
- `HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=0|1` (default: 0)
- Normalize JsonFrag output for stable canaries (const grouping, phi placement, numeric canonicalization).
- `HAKO_MIR_BUILDER_LOOP_RETURN=string|map` (default: string)
- Controls the return type of the loop adapter JsonFrag path. `string` returns MIR(JSON) text; `map` returns a `MapBox` like `{ mir: <string> }`.
Provider diagnostics
- `HAKO_PROVIDER_TRACE=0|1` (default: 0)
- When 1, forces provider selection tags like `[provider/select:FileBox ring=1 src=static]` to appear even under `NYASH_JSON_ONLY=1`.

View File

@ -0,0 +1,43 @@
# Phase 21.5 — Optimization Readiness (MirBuilder + Runtime)
Goal
- Target: ≥ 80% of equivalent C for core microbenches同一アルゴリズム、最適化ON
- Scope: Nyash AOT/LLVM ライン(ハーネス or EXE、ホットパスの構造最適化挙動不変
- Guard: 既定挙動/ABI不変、devトグルでの可視化と段階導入、ロールバック容易。
Readiness (done)
- Normalizer を MirBuilder 本線/Min に既定OFFで配線、ON時のみタグ静音トグル付
- f64 正規化の共通化JsonNumberCanonical・phi整列・ret値明示・const重複排除。
- Canary: 冪等 / crossblock nodedupe / rcパリティif/loop/binop/ f64指数/0.0/末尾0/ phimany/nested
Plan
1) Baseline & Harness
- 代表ベンチLoopSum / StrLen / BoxAllocで C baseline と Nyash(LLVM) を比較。
- 計測を tools/perf/microbench.sh に集約。`--runs N` で中央値/平均を算出。
2) Hotspot Discovery
- Builder 出力の冗長箇所const/compare連打、不要な一時値、Runtime の alloc/型分岐を観測。
- 代表ケースで alloc 回数/branch 回数をロギングdevトグル下、既定OFF
3) Small-step Optimization構造優先・挙動不変
- Builder 側: テキスト結合の抑制集約バッファ、重複constの先頭集約既に導入済
- Runtime 側: 軽量 fastpath整数演算/比較の直通、Box 生存期間短縮(スコープ明確化)。
4) Acceptance
- 代表ベンチ3種で C の ≥80%(中央値)を達成。変動が大きい環境では ±10%を許容しつつCIはスキップ扱い。
How to run (examples)
- Quick with Normalizer ON重いEXEありのため timeout 120 推奨):
- `HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1 ./tools/smokes/v2/run.sh --profile quick --timeout 120`
- MicrobenchC vs Nyash/LLVM ハーネス):
- `tools/perf/microbench.sh --case loop --n 5000000 --runs 5`
- `tools/perf/microbench.sh --case strlen --n 2000000 --runs 5`
- `tools/perf/microbench.sh --case box --n 100000 --runs 5`
- VM runtime countersdev 診断、既定OFF:
- `NYASH_VM_STATS=1 ./target/release/hakorune --backend vm apps/tests/CASE.hako``[vm/stats] inst=… compare=… branch=…` を出力
Toggles
- `HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1` — 正規化ON既定OFF
- `HAKO_MIR_BUILDER_NORMALIZE_TAG=1` — タグ出力(既定静音)
- `SMOKES_DEV_NORMALIZE=1` — devヘルパから正規化ON注入quick限定ON例は test_runner にコメント記載)
Notes
- ベンチは OS/CPU/周辺負荷に依存するため、CIでの厳格判定は避け、ローカル/任意ジョブで傾向を確認。
- 構造最適化優先(条件分岐/インライン化での挙動差を避ける)。

View File

@ -52,6 +52,18 @@ static box MirBuilderBox {
local internal = env.get("HAKO_MIR_BUILDER_INTERNAL") local internal = env.get("HAKO_MIR_BUILDER_INTERNAL")
local internal_on = (internal == null) || (("" + internal) == "1") local internal_on = (internal == null) || (("" + internal) == "1")
if internal_on == 1 { if internal_on == 1 {
// Dev-only: force loop JSONFrag minimal output (pre-optimization bring-up)
// Guard: HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG=1
{
local f = env.get("HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG")
if f != null && ("" + f) == "1" {
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[" +
"{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"compare\",\"operation\":\"<\",\"lhs\":1,\"rhs\":2,\"dst\":3},{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
"{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," +
"{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}"
return norm_if(mir)
}
}
// Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1, // Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1,
// iterate PatternRegistryBox.candidates() and dispatch by name. // iterate PatternRegistryBox.candidates() and dispatch by name.
// NOTE: using/alias は prelude で解決される(位置に依存しない)。 // NOTE: using/alias は prelude で解決される(位置に依存しない)。

View File

@ -1,22 +1,12 @@
# MirBuilder Internal Boxes — Minimal Shapes (Phase 20.34) MirBuilder Internals — Toggle Aggregation
Responsibility - Use `builder_config_box.hako` (`hako.mir.builder.internal.builder_config`) to read all `HAKO_MIR_BUILDER_*` toggles.
- Provide small, readable boxes that lower a very small subset of Program(JSON v0) - Do not call `env.get` directly in lowers; prefer helper methods like:
into MIR(JSON v0) strings without heavy parsing. FailFast with stable tags. - `trace_enabled()`, `debug_enabled()`
- `internal_on()`, `registry_on()`, `registry_only()`
- `loop_jsonfrag_on()`, `jsonfrag_normalize_on()`, `skip_loops_on()`
- `loop_adapter_return_mode()``string` (default) or `map`
Boxes Notes
- `prog_scan_box.hako` — tiny helpers for string scanning: find, skip spaces, read quoted/op/int values. - JsonFrag emission is kept default OFF and used for structural observation only. Semantics are prioritized by the normal path.
- `lower_return_int_box.hako` — Return(Int N) → const(i64 N) + ret.
- `lower_return_binop_box.hako` — Return(Binary(Int,Int) op {+,-,*,/}) → const, const, binop, ret.
- `lower_if_compare_box.hako` — If(Compare(Int,Int) then Return(Int) else Return(Int)) → compare, branch, ret×2.
- `lower_loop_simple_box.hako` — Loop(Compare i<limit) counting loopi=0..limit LoopFormBox.loop_count に委譲
- `lower_loop_sum_bc_box.hako` Loop sum + break/continue 哲学i==break break / i==skip continue)→ LoopFormBox.build("sum_bc", ...)。
Policy
- No execution or I/O; emit-only. Keep shapes minimal and obvious. For unsupported inputs, print
`[mirbuilder/internal/unsupported] ...` and return null.
LoopForm param
- LoopFormBox.build(mode, limit, skip, break)
- mode: "count"i を返す/ "sum_bc"sum の合流を Exit PHI で返す
- limit/skip/break i64 として扱うskip/break null の場合`skip=2` / `break=limit` を既定とする

View File

@ -0,0 +1,44 @@
// builder_config_box.hako — Centralized env/toggles for MirBuilder
// Purpose: avoid scattered env.get calls. All HAKO_MIR_BUILDER_* toggles are read here.
static box BuilderConfigBox {
// Generic on/off reader for HAKO_MIR_BUILDER_* flags
_is_on(key) {
local v = env.get(key)
if v == null { return 0 }
local s = "" + v
if s == "1" || s == "true" || s == "on" { return 1 }
return 0
}
// Trace controls
trace_enabled() { return self._is_on("HAKO_MIR_BUILDER_TRACE") }
debug_enabled() { return self._is_on("HAKO_MIR_BUILDER_DEBUG") || self._is_on("NYASH_CLI_VERBOSE") }
// Registry and internal lowers
internal_on() {
local v = env.get("HAKO_MIR_BUILDER_INTERNAL")
// default ON
return (v == null) || ("" + v == "1")
}
registry_on() {
local v = env.get("HAKO_MIR_BUILDER_REGISTRY")
// default ON
return (v == null) || ("" + v == "1")
}
registry_only() { return env.get("HAKO_MIR_BUILDER_REGISTRY_ONLY") }
// Loop/JsonFrag related
loop_jsonfrag_on() { return self._is_on("HAKO_MIR_BUILDER_LOOP_JSONFRAG") }
jsonfrag_normalize_on() { return self._is_on("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE") }
skip_loops_on() { return self._is_on("HAKO_MIR_BUILDER_SKIP_LOOPS") }
// Return-mode for loop adapter: "string" (default) | "map"
loop_adapter_return_mode() {
local m = env.get("HAKO_MIR_BUILDER_LOOP_RETURN")
if m == null { return "string" }
local s = "" + m
if s == "string" { return "string" }
return "map"
}
}

View File

@ -39,6 +39,8 @@ static box JsonFragNormalizerBox {
local const_list = new ArrayBox() local const_list = new ArrayBox()
local others_list = new ArrayBox() local others_list = new ArrayBox()
local seen = new MapBox() // signature -> 1 local seen = new MapBox() // signature -> 1
// Dev-only: purify flag to drop builder-side allocations (e.g., newbox) from JSONFrag output
local purify = 0; { local pv = env.get("HAKO_MIR_BUILDER_JSONFRAG_PURIFY"); if pv != null { local pvs = "" + pv; if pvs == "1" { purify = 1 } } }
loop(i < n) { loop(i < n) {
guard = guard + 1 guard = guard + 1
if guard > 4096 { break } if guard > 4096 { break }
@ -52,6 +54,9 @@ static box JsonFragNormalizerBox {
i = ob_end + 1 i = ob_end + 1
// classify by op // classify by op
local op = JsonFragBox.get_str(obj, "op") local op = JsonFragBox.get_str(obj, "op")
if purify == 1 {
if op == "newbox" { continue }
}
if op == "phi" { if op == "phi" {
phi_list.push(obj) phi_list.push(obj)
continue continue

View File

@ -2,6 +2,8 @@
// Purpose: centralize MapBox creation/set so that we can later swap to JsonFrag-based // Purpose: centralize MapBox creation/set so that we can later swap to JsonFrag-based
// construction without touching all callers. // construction without touching all callers.
using "hako.mir.builder.internal.builder_config" as BuilderConfigBox
static box LoopOptsBox { static box LoopOptsBox {
new_map() { new_map() {
return new MapBox() return new MapBox()
@ -16,8 +18,7 @@ static box LoopOptsBox {
using selfhost.shared.common.string_helpers as StringHelpers using selfhost.shared.common.string_helpers as StringHelpers
using "hako.mir.builder.internal.jsonfrag_normalizer" as JsonFragNormalizerBox using "hako.mir.builder.internal.jsonfrag_normalizer" as JsonFragNormalizerBox
// Opt-in: minimal MIR(JSON) construction for structure validation // Opt-in: minimal MIR(JSON) construction for structure validation
local jf = env.get("HAKO_MIR_BUILDER_LOOP_JSONFRAG") if BuilderConfigBox.loop_jsonfrag_on() == 1 {
if jf != null && ("" + jf) == "1" {
// Read limit if present, else default to 0 (safe) // Read limit if present, else default to 0 (safe)
local limit = opts.get("limit"); if limit == null { limit = 0 } local limit = opts.get("limit"); if limit == null { limit = 0 }
// Normalize to string // Normalize to string
@ -31,11 +32,15 @@ static box LoopOptsBox {
"{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," + "{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
"{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," + "{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," +
"{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}" "{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}"
print("[mirbuilder/internal/loop:jsonfrag]") if BuilderConfigBox.trace_enabled() == 1 { print("[mirbuilder/internal/loop:jsonfrag]") }
// Optional: normalization (dev toggle) // Optional: normalization (dev toggle)
local norm = env.get("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE") if BuilderConfigBox.jsonfrag_normalize_on() == 1 { mir = JsonFragNormalizerBox.normalize_all(mir) }
if norm != null && ("" + norm) == "1" { // Return mode: default string, optionally wrap into Map
return JsonFragNormalizerBox.normalize_all(mir) local mode = BuilderConfigBox.loop_adapter_return_mode()
if mode == "map" {
local m = self.new_map()
m = self.put(m, "mir", mir)
return m
} }
return mir return mir
} }

View File

@ -8,6 +8,7 @@ using selfhost.shared.json.utils.json_frag as JsonFragBox
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
using "hako.mir.builder.internal.loop_scan" as LoopScanBox using "hako.mir.builder.internal.loop_scan" as LoopScanBox
using "hako.mir.builder.internal.loop_opts_adapter" as LoopOptsBox using "hako.mir.builder.internal.loop_opts_adapter" as LoopOptsBox
using "hako.mir.builder.internal.builder_config" as BuilderConfigBox
static box LowerLoopSimpleBox { static box LowerLoopSimpleBox {
try_lower(program_json) { try_lower(program_json) {
@ -59,8 +60,7 @@ static box LowerLoopSimpleBox {
// JsonFrag 直組立opt-in: HAKO_MIR_BUILDER_LOOP_JSONFRAG=1 // JsonFrag 直組立opt-in: HAKO_MIR_BUILDER_LOOP_JSONFRAG=1
{ {
local jf = env.get("HAKO_MIR_BUILDER_LOOP_JSONFRAG") if BuilderConfigBox.loop_jsonfrag_on() == 1 {
if jf != null && ("" + jf) == "1" {
// Minimal MIR(JSON) with compare + branch + ret構造検証用 // Minimal MIR(JSON) with compare + branch + ret構造検証用
// Note: semanticsは簡略canaryはトークン検出のみ // Note: semanticsは簡略canaryはトークン検出のみ
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[" + local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[" +
@ -71,7 +71,7 @@ static box LowerLoopSimpleBox {
"{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," + "{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
"{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," + "{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," +
"{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}" "{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}"
print("[mirbuilder/internal/loop:jsonfrag]") if BuilderConfigBox.trace_enabled() == 1 { print("[mirbuilder/internal/loop:jsonfrag]") }
return mir return mir
} }
} }

View File

@ -257,6 +257,11 @@ impl MirInterpreter {
if Self::trace_enabled() { if Self::trace_enabled() {
eprintln!("[vm-trace] inst bb={:?} {:?}", block.id, inst); eprintln!("[vm-trace] inst bb={:?} {:?}", block.id, inst);
} }
// Dev counters: count non-phi instructions and compares
self.inst_count = self.inst_count.saturating_add(1);
if let MirInstruction::Compare { .. } = inst {
self.compare_count = self.compare_count.saturating_add(1);
}
self.execute_instruction(inst)?; self.execute_instruction(inst)?;
} }
Ok(()) Ok(())
@ -281,6 +286,8 @@ impl MirInterpreter {
then_bb, then_bb,
else_bb, else_bb,
}) => { }) => {
// Dev counter: count branch terminators actually evaluated
self.branch_count = self.branch_count.saturating_add(1);
let cond = self.reg_load(*condition)?; let cond = self.reg_load(*condition)?;
let branch = to_bool_vm(&cond).map_err(VMError::TypeError)?; let branch = to_bool_vm(&cond).map_err(VMError::TypeError)?;
let target = if branch { *then_bb } else { *else_bb }; let target = if branch { *then_bb } else { *else_bb };

View File

@ -37,6 +37,10 @@ pub struct MirInterpreter {
pub(super) static_boxes: HashMap<String, crate::instance_v2::InstanceBox>, pub(super) static_boxes: HashMap<String, crate::instance_v2::InstanceBox>,
// Static box declarations (metadata for creating instances) // Static box declarations (metadata for creating instances)
pub(super) static_box_decls: HashMap<String, crate::core::model::BoxDeclaration>, pub(super) static_box_decls: HashMap<String, crate::core::model::BoxDeclaration>,
// Lightweight dev counters (opt-in print via NYASH_VM_STATS=1)
pub(super) inst_count: u64,
pub(super) branch_count: u64,
pub(super) compare_count: u64,
} }
impl MirInterpreter { impl MirInterpreter {
@ -51,9 +55,17 @@ impl MirInterpreter {
last_inst: None, last_inst: None,
static_boxes: HashMap::new(), static_boxes: HashMap::new(),
static_box_decls: HashMap::new(), static_box_decls: HashMap::new(),
inst_count: 0,
branch_count: 0,
compare_count: 0,
} }
} }
/// Return (inst_count, branch_count, compare_count)
pub fn stats_counters(&self) -> (u64, u64, u64) {
(self.inst_count, self.branch_count, self.compare_count)
}
/// Register static box declarations (called from vm.rs during setup) /// Register static box declarations (called from vm.rs during setup)
pub fn register_static_box_decl(&mut self, name: String, decl: crate::core::model::BoxDeclaration) { pub fn register_static_box_decl(&mut self, name: String, decl: crate::core::model::BoxDeclaration) {
self.static_box_decls.insert(name, decl); self.static_box_decls.insert(name, decl);

View File

@ -4,5 +4,6 @@
pub mod env; pub mod env;
pub mod nyash_toml_v2; pub mod nyash_toml_v2;
pub mod provider_env;
pub use nyash_toml_v2::{BoxTypeConfig, LibraryDefinition, MethodDefinition, NyashConfigV2}; pub use nyash_toml_v2::{BoxTypeConfig, LibraryDefinition, MethodDefinition, NyashConfigV2};

View File

@ -0,0 +1,47 @@
//! Provider-related environment readers (centralized)
//!
//! Minimizes scattered `std::env::var` calls across the runtime by
//! providing a small typed surface for provider selection knobs.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProviderPolicy {
/// Current default: prefer plugin/dynamic providers by priority; use ring-1 as fallback
StrictPluginFirst,
/// Prefer ring-1 (static/core-ro) for stability/CI; fall back to plugin if unavailable
SafeCoreFirst,
/// Alias to SafeCoreFirst for future extension
StaticPreferred,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FileBoxMode { Auto, CoreRo, PluginOnly }
/// Read global provider policy (affects Auto mode only)
pub fn provider_policy_from_env() -> ProviderPolicy {
match std::env::var("HAKO_PROVIDER_POLICY").unwrap_or_else(|_| "strict-plugin-first".to_string()).as_str() {
"safe-core-first" => ProviderPolicy::SafeCoreFirst,
"static-preferred" => ProviderPolicy::StaticPreferred,
_ => ProviderPolicy::StrictPluginFirst,
}
}
/// Read FileBox mode from environment variables
pub fn filebox_mode_from_env() -> FileBoxMode {
match std::env::var("NYASH_FILEBOX_MODE").unwrap_or_else(|_| "auto".to_string()).as_str() {
"core-ro" => FileBoxMode::CoreRo,
"plugin-only" => FileBoxMode::PluginOnly,
_ => {
if std::env::var("NYASH_DISABLE_PLUGINS").as_deref() == Ok("1") {
FileBoxMode::CoreRo
} else { FileBoxMode::Auto }
}
}
}
/// Allow core-ro fallback override even if Fail-Fast is ON when:
/// - JSON-only pipeline is active (quiet structured I/O), or
/// - Explicit NYASH_FILEBOX_ALLOW_FALLBACK=1 is set.
pub fn allow_filebox_fallback_override(quiet_pipe: bool) -> bool {
quiet_pipe || crate::config::env::env_bool("NYASH_FILEBOX_ALLOW_FALLBACK")
}

View File

@ -5,6 +5,6 @@ This directory hosts ring1 (core providers) and related documentation.
Goals Goals
- Centralize provider responsibilities and selection policy. - Centralize provider responsibilities and selection policy.
- Keep ring1 minimal and reproducible (static or in-tree providers). - Keep ring1 minimal and reproducible (static or in-tree providers).
- Registry groups factories by Box type (e.g., "FileBox") to allow future expansion (Array/Map/Console/Path) without changing selection policy.
See also: src/providers/ring1/ and docs/architecture/RINGS.md See also: src/providers/ring1/ and docs/architecture/RINGS.md

View File

@ -0,0 +1,6 @@
ring1/array — Placeholder (Scope/Policy)
- Purpose: future home for minimal, trusted ArrayBox providers (if introduced).
- Policy: ring1 is static/minimal only; no plugin (ring2) dependencies.
- Do not implement business logic here until design is finalized.

View File

@ -0,0 +1,6 @@
ring1/console — Placeholder (Scope/Policy)
- Purpose: future home for minimal, trusted Console providers (if introduced).
- Policy: ring1 is static/minimal only; no plugin (ring2) dependencies.
- Do not implement business logic here until design is finalized.

View File

@ -0,0 +1,6 @@
ring1/map — Placeholder (Scope/Policy)
- Purpose: future home for minimal, trusted MapBox providers (if introduced).
- Policy: ring1 is static/minimal only; no plugin (ring2) dependencies.
- Do not implement business logic here until design is finalized.

View File

@ -0,0 +1,6 @@
ring1/path — Placeholder (Scope/Policy)
- Purpose: future home for minimal, trusted PathBox providers (if introduced).
- Policy: ring1 is static/minimal only; no plugin (ring2) dependencies.
- Do not implement business logic here until design is finalized.

View File

@ -0,0 +1,28 @@
//! Common diagnostics helpers (concise, centralized)
/// Whether provider logs should be emitted under current policy.
/// quiet_pipe usually reflects NYASH_JSON_ONLY; allowing override with HAKO_PROVIDER_TRACE=1.
pub fn provider_log_enabled(quiet_pipe: bool) -> bool {
!quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1")
}
/// Emit a consistent provider-registry info line.
pub fn provider_log_info(msg: &str) {
eprintln!("[provider-registry] {}", msg);
}
/// Emit the provider selection tag in a stable shape.
pub fn provider_log_select(box_name: &str, ring: &str, source: &str, caps: Option<&str>) {
match caps {
Some(c) if !c.is_empty() => {
eprintln!("[provider/select:{} ring={} src={} caps={}]", box_name, ring, source, c);
}
_ => {
eprintln!("[provider/select:{} ring={} src={}]", box_name, ring, source);
}
}
}
/// Emit a Fail-Fast tag for provider fallback/selection errors.
pub fn failfast_provider(reason: &str) { eprintln!("[failfast/provider/{}]", reason); }

View File

@ -14,3 +14,4 @@ pub mod core_bridge;
pub mod hako; pub mod hako;
pub mod plugin_guard; pub mod plugin_guard;
pub mod provider_registry; pub mod provider_registry;
pub mod diag;

View File

@ -1,26 +1,21 @@
//! Provider registry: selects concrete providers for core resources (e.g. FileBox). //! Provider registry: selects concrete providers for core resources (e.g. FileBox).
//! SSOT (Single Source of Truth) for provider selection via ProviderFactory registration. //! SSOT (Single Source of Truth) for provider selection via ProviderFactory registration.
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock}; use std::sync::{Arc, Mutex, OnceLock};
use crate::boxes::file::provider::FileIo;
use crate::boxes::file::core_ro::CoreRoFileIo; use crate::boxes::file::core_ro::CoreRoFileIo;
use crate::boxes::file::provider::FileIo;
use crate::config::provider_env::{self, ProviderPolicy};
use crate::runner::modes::common_util::diag;
/// Provider selection policy (global). Applied when mode=Auto. // Policy/Mode are provided by config::provider_env (centralized)
#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub use crate::config::provider_env::FileBoxMode;
enum ProviderPolicy {
/// Current default: prefer plugin/dynamic providers by priority; use ring-1 as fallback
StrictPluginFirst,
/// Prefer ring-1 (static/core-ro) for stability/CI; fall back to plugin if unavailable
SafeCoreFirst,
/// Alias to SafeCoreFirst for future extension
StaticPreferred,
}
#[allow(dead_code)] /// Factory for creating providers for a specific Box type.
pub enum FileBoxMode { Auto, CoreRo, PluginOnly } /// Note: Currently specialized for FileBox via `FileIo`.
/// The registry is structured as BoxName → [Factory], enabling future
/// Factory for creating FileIo providers /// extension to other Box kinds without changing the selection policy surface.
pub trait ProviderFactory: Send + Sync { pub trait ProviderFactory: Send + Sync {
fn box_name(&self) -> &str; fn box_name(&self) -> &str;
fn create_provider(&self) -> Arc<dyn FileIo>; fn create_provider(&self) -> Arc<dyn FileIo>;
@ -30,13 +25,17 @@ pub trait ProviderFactory: Send + Sync {
} }
} }
/// Global registry of provider factories /// Global registry of provider factories, grouped by Box name
static PROVIDER_FACTORIES: OnceLock<Mutex<Vec<Arc<dyn ProviderFactory>>>> = OnceLock::new(); static PROVIDER_FACTORIES: OnceLock<Mutex<HashMap<String, Vec<Arc<dyn ProviderFactory>>>>> =
OnceLock::new();
/// Register a provider factory (called by builtin/dynamic loaders) /// Register a provider factory (called by builtin/dynamic loaders)
pub fn register_provider_factory(factory: Arc<dyn ProviderFactory>) { pub fn register_provider_factory(factory: Arc<dyn ProviderFactory>) {
let registry = PROVIDER_FACTORIES.get_or_init(|| Mutex::new(Vec::new())); let registry = PROVIDER_FACTORIES
registry.lock().unwrap().push(factory); .get_or_init(|| Mutex::new(HashMap::new()));
let mut guard = registry.lock().unwrap();
let key = factory.box_name().to_string();
guard.entry(key).or_default().push(factory);
} }
/// Builtin ring1 FileBox provider (corero) — always available, lowest priority /// Builtin ring1 FileBox provider (corero) — always available, lowest priority
@ -51,37 +50,20 @@ impl ProviderFactory for CoreRoFileProviderFactory {
/// Ensure ring1 (corero) provider is present in the registry /// Ensure ring1 (corero) provider is present in the registry
fn ensure_builtin_file_provider_registered() { fn ensure_builtin_file_provider_registered() {
let reg = PROVIDER_FACTORIES.get_or_init(|| Mutex::new(Vec::new())); let reg = PROVIDER_FACTORIES
.get_or_init(|| Mutex::new(HashMap::new()));
let mut guard = reg.lock().unwrap(); let mut guard = reg.lock().unwrap();
// If at least one FileBox provider exists, we still keep ring1 present for safety; avoid duplicates by checking any corero present by priority let list = guard.entry("FileBox".to_string()).or_default();
let has_core_ro = guard.iter().any(|f| f.box_name() == "FileBox" && f.priority() <= -100); // keep ring1 present for safety; avoid duplicates by checking any corero present by priority
let has_core_ro = list.iter().any(|f| f.priority() <= -100);
if !has_core_ro { if !has_core_ro {
guard.push(Arc::new(CoreRoFileProviderFactory)); list.push(Arc::new(CoreRoFileProviderFactory));
} }
} }
/// Read global provider policy (affects Auto mode only) /// Backward-compat public readers for existing callers (if any)
fn read_provider_policy_from_env() -> ProviderPolicy {
match std::env::var("HAKO_PROVIDER_POLICY").unwrap_or_else(|_| "strict-plugin-first".to_string()).as_str() {
"safe-core-first" => ProviderPolicy::SafeCoreFirst,
"static-preferred" => ProviderPolicy::StaticPreferred,
_ => ProviderPolicy::StrictPluginFirst,
}
}
/// Read FileBox mode from environment variables
#[allow(dead_code)] #[allow(dead_code)]
pub fn read_filebox_mode_from_env() -> FileBoxMode { pub fn read_filebox_mode_from_env() -> FileBoxMode { provider_env::filebox_mode_from_env() }
match std::env::var("NYASH_FILEBOX_MODE").unwrap_or_else(|_| "auto".to_string()).as_str() {
"core-ro" => FileBoxMode::CoreRo,
"plugin-only" => FileBoxMode::PluginOnly,
_ => {
if std::env::var("NYASH_DISABLE_PLUGINS").as_deref() == Ok("1") {
FileBoxMode::CoreRo
} else { FileBoxMode::Auto }
}
}
}
/// Select provider based on mode and registered factories (SSOT) /// Select provider based on mode and registered factories (SSOT)
#[allow(dead_code)] #[allow(dead_code)]
@ -94,13 +76,14 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
match mode { match mode {
FileBoxMode::Auto => { FileBoxMode::Auto => {
// Selection by global policy // Selection by global policy
let policy = read_provider_policy_from_env(); let policy = provider_env::provider_policy_from_env();
if let Some(reg) = registry { if let Some(reg) = registry {
let mut factories: Vec<_> = reg.lock().unwrap() let mut factories: Vec<_> = reg
.iter() .lock()
.filter(|f| f.box_name() == "FileBox" && f.is_available()) .unwrap()
.cloned() .get("FileBox")
.collect(); .map(|v| v.iter().filter(|f| f.is_available()).cloned().collect())
.unwrap_or_else(|| Vec::new());
// Sort by priority (descending); plugin providers should rank higher than ring-1 (priority -100) // Sort by priority (descending); plugin providers should rank higher than ring-1 (priority -100)
factories.sort_by(|a, b| b.priority().cmp(&a.priority())); factories.sort_by(|a, b| b.priority().cmp(&a.priority()));
@ -109,9 +92,9 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
match policy { match policy {
ProviderPolicy::StrictPluginFirst => { ProviderPolicy::StrictPluginFirst => {
if let Some(factory) = factories.first() { if let Some(factory) = factories.first() {
if !quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1") { if diag::provider_log_enabled(quiet_pipe) {
eprintln!("[provider-registry] FileBox: using registered provider (priority={})", factory.priority()); diag::provider_log_info(&format!("FileBox: using registered provider (priority={})", factory.priority()));
eprintln!("[provider/select:FileBox ring=plugin src=dynamic]"); diag::provider_log_select("FileBox", "plugin", "dynamic", None);
} }
return factory.create_provider(); return factory.create_provider();
} }
@ -119,17 +102,17 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
ProviderPolicy::SafeCoreFirst | ProviderPolicy::StaticPreferred => { ProviderPolicy::SafeCoreFirst | ProviderPolicy::StaticPreferred => {
// Prefer ring-1 (priority <= -100) // Prefer ring-1 (priority <= -100)
if let Some(core_ro) = factories.iter().find(|f| f.priority() <= -100) { if let Some(core_ro) = factories.iter().find(|f| f.priority() <= -100) {
if !quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1") { if diag::provider_log_enabled(quiet_pipe) {
eprintln!("[provider-registry] FileBox: using core-ro (policy)"); diag::provider_log_info("FileBox: using core-ro (policy)");
eprintln!("[provider/select:FileBox ring=1 src=static caps=[read]]"); diag::provider_log_select("FileBox", "1", "static", Some("[read]"));
} }
return core_ro.create_provider(); return core_ro.create_provider();
} }
// Fallback to first available (plugin) // Fallback to first available (plugin)
if let Some(factory) = factories.first() { if let Some(factory) = factories.first() {
if !quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1") { if diag::provider_log_enabled(quiet_pipe) {
eprintln!("[provider-registry] FileBox: using registered provider (priority={})", factory.priority()); diag::provider_log_info(&format!("FileBox: using registered provider (priority={})", factory.priority()));
eprintln!("[provider/select:FileBox ring=plugin src=dynamic]"); diag::provider_log_select("FileBox", "plugin", "dynamic", None);
} }
return factory.create_provider(); return factory.create_provider();
} }
@ -142,20 +125,18 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
// - When JSONonly pipeline is active (quiet structured I/O), or // - When JSONonly pipeline is active (quiet structured I/O), or
// - When NYASH_FILEBOX_ALLOW_FALLBACK=1 is set, // - When NYASH_FILEBOX_ALLOW_FALLBACK=1 is set,
// always use corero provider even if FailFast is ON. // always use corero provider even if FailFast is ON.
let allow_fb_override = let allow_fb_override = provider_env::allow_filebox_fallback_override(quiet_pipe);
crate::config::env::env_bool("NYASH_JSON_ONLY") ||
crate::config::env::env_bool("NYASH_FILEBOX_ALLOW_FALLBACK");
if crate::config::env::fail_fast() && !allow_fb_override { if crate::config::env::fail_fast() && !allow_fb_override {
eprintln!("[failfast/provider/filebox:auto-fallback-blocked]"); diag::failfast_provider("filebox:auto-fallback-blocked");
panic!("Fail-Fast: FileBox provider fallback is disabled (NYASH_FAIL_FAST=0 or NYASH_FILEBOX_ALLOW_FALLBACK=1 to override)"); panic!("Fail-Fast: FileBox provider fallback is disabled (NYASH_FAIL_FAST=0 or NYASH_FILEBOX_ALLOW_FALLBACK=1 to override)");
} else { } else {
if !quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1") { if diag::provider_log_enabled(quiet_pipe) {
eprintln!( diag::provider_log_info(&format!(
"[provider-registry] FileBox: using core-ro fallback{}", "FileBox: using core-ro fallback{}",
if allow_fb_override { " (override)" } else { "" } if allow_fb_override { " (override)" } else { "" }
); ));
eprintln!("[provider/select:FileBox ring=1 src=static caps=[read]]"); diag::provider_log_select("FileBox", "1", "static", Some("[read]"));
} }
Arc::new(CoreRoFileIo::new()) Arc::new(CoreRoFileIo::new())
} }
@ -163,18 +144,19 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
FileBoxMode::PluginOnly => { FileBoxMode::PluginOnly => {
// Try only registered providers, Fail-Fast if none available // Try only registered providers, Fail-Fast if none available
if let Some(reg) = registry { if let Some(reg) = registry {
let mut factories: Vec<_> = reg.lock().unwrap() let mut factories: Vec<_> = reg
.iter() .lock()
.filter(|f| f.box_name() == "FileBox" && f.is_available()) .unwrap()
.cloned() .get("FileBox")
.collect(); .map(|v| v.iter().filter(|f| f.is_available()).cloned().collect())
.unwrap_or_else(|| Vec::new());
factories.sort_by(|a, b| b.priority().cmp(&a.priority())); factories.sort_by(|a, b| b.priority().cmp(&a.priority()));
if let Some(factory) = factories.first() { if let Some(factory) = factories.first() {
if !quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1") { if diag::provider_log_enabled(quiet_pipe) {
eprintln!("[provider-registry] FileBox: using plugin-only provider (priority={})", factory.priority()); diag::provider_log_info(&format!("FileBox: using plugin-only provider (priority={})", factory.priority()));
eprintln!("[provider/select:FileBox ring=plugin src=dynamic]"); diag::provider_log_select("FileBox", "plugin", "dynamic", None);
} }
return factory.create_provider(); return factory.create_provider();
} }
@ -184,9 +166,9 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
} }
FileBoxMode::CoreRo => { FileBoxMode::CoreRo => {
// Always use core-ro, ignore registry // Always use core-ro, ignore registry
if !quiet_pipe || std::env::var("HAKO_PROVIDER_TRACE").as_deref() == Ok("1") { if diag::provider_log_enabled(quiet_pipe) {
eprintln!("[provider-registry] FileBox: using core-ro (forced)"); diag::provider_log_info("FileBox: using core-ro (forced)");
eprintln!("[provider/select:FileBox ring=1 src=static caps=[read]]"); diag::provider_log_select("FileBox", "1", "static", Some("[read]"));
} }
Arc::new(CoreRoFileIo::new()) Arc::new(CoreRoFileIo::new())
} }

View File

@ -429,6 +429,12 @@ impl NyashRunner {
0 0
}; };
// Optional: print lightweight VM counters for diagnostics
if crate::config::env::env_bool("NYASH_VM_STATS") {
let (inst, br, cmp) = vm.stats_counters();
eprintln!("[vm/stats] inst={} compare={} branch={}", inst, cmp, br);
}
// Quiet mode: suppress "RC:" output for JSON-only pipelines // Quiet mode: suppress "RC:" output for JSON-only pipelines
if !quiet_pipe { if !quiet_pipe {
println!("RC: {}", exit_code); println!("RC: {}", exit_code);

23
tools/lib/canary.sh Normal file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# canary.sh — Common helpers for canary scripts (JSON extract / token checks)
# Library only; safe to source from any test harness or standalone script.
# Extract content between [MIR_BEGIN] and [MIR_END] tags from stdin
extract_mir_between_tags() {
awk '/\[MIR_BEGIN\]/{f=1;next}/\[MIR_END\]/{f=0}f'
}
# Require that all given tokens appear in stdin; prints a FAIL line and
# exits with non-zero status if any token is missing.
require_tokens() {
local content
content=$(cat)
for tk in "$@"; do
if ! grep -Fq -- "$tk" <<<"$content"; then
echo "[FAIL] token missing: $tk" >&2
return 1
fi
done
return 0
}

View File

@ -12,7 +12,7 @@ Usage: tools/ny_mir_builder.sh [--in <file>|--stdin] [--emit {obj|exe|ll|json}]
Notes: Notes:
- This is a Phase-15 shell wrapper that leverages the nyash LLVM harness. - This is a Phase-15 shell wrapper that leverages the nyash LLVM harness.
- Input must be Nyash JSON IR (v0/v1). When --stdin is used, reads from stdin. - Input must be Nyash JSON IR (v0/v1). When --stdin is used, reads from stdin.
- For --emit exe, NyRT must be built (crates/nyrt). Use default paths if --nyrt omitted. - For --emit exe, kernel runtime must be built (crates/nyash_kernel). Use default paths if --nyrt omitted.
USAGE USAGE
} }
@ -90,7 +90,7 @@ if [[ "$SKIP_BUILD" != "1" ]]; then
(cd "$(dirname "$0")/.." && timeout "$BUILD_TIMEOUT" cargo build --release -j 24 -p nyash-llvm-compiler >/dev/null) || true (cd "$(dirname "$0")/.." && timeout "$BUILD_TIMEOUT" cargo build --release -j 24 -p nyash-llvm-compiler >/dev/null) || true
fi fi
if [[ "$EMIT" == "exe" ]]; then if [[ "$EMIT" == "exe" ]]; then
(cd crates/nyrt && timeout "$BUILD_TIMEOUT" cargo build --release -j 24 >/dev/null) (cd crates/nyash_kernel && timeout "$BUILD_TIMEOUT" cargo build --release -j 24 >/dev/null)
fi fi
fi fi

216
tools/perf/microbench.sh Normal file
View File

@ -0,0 +1,216 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
BIN="$ROOT/target/release/hakorune"
usage() { echo "Usage: $0 --case {loop|strlen|box} [--n N] [--runs R] [--backend {llvm|vm}] [--exe]"; }
CASE="loop"; N=5000000; RUNS=5; BACKEND="llvm"; EXE_MODE=0
while [[ $# -gt 0 ]]; do
case "$1" in
--case) CASE="$2"; shift 2;;
--n) N="$2"; shift 2;;
--runs) RUNS="$2"; shift 2;;
--backend) BACKEND="$2"; shift 2;;
--exe) EXE_MODE=1; shift 1;;
--help|-h) usage; exit 0;;
*) echo "Unknown arg: $1"; usage; exit 2;;
esac
done
if [[ ! -x "$BIN" ]]; then echo "[FAIL] hakorune not built: $BIN" >&2; exit 2; fi
bench_hako() {
local file="$1"; local backend="$2"; shift 2
local start end
start=$(date +%s%N)
if [[ "$backend" = "llvm" ]]; then
# Ensure ny-llvmc exists; build if missing
if [[ ! -x "$ROOT/target/release/ny-llvmc" ]]; then
(cargo build -q --release -p nyash-llvm-compiler >/dev/null 2>&1) || true
fi
PYTHONPATH="${PYTHONPATH:-$ROOT}" \
NYASH_NY_LLVM_COMPILER="${NYASH_NY_LLVM_COMPILER:-$ROOT/target/release/ny-llvmc}" \
NYASH_EMIT_EXE_NYRT="${NYASH_EMIT_EXE_NYRT:-$ROOT/target/release}" \
NYASH_LLVM_USE_HARNESS=1 "$BIN" --backend llvm "$file" >/dev/null 2>&1
else
"$BIN" --backend vm "$file" >/dev/null 2>&1
fi
end=$(date +%s%N)
echo $(( (end - start)/1000000 ))
}
bench_c() {
local csrc="$1"; local exe="$2"
cc -O3 -march=native -o "$exe" "$csrc"
local start end
start=$(date +%s%N)
"$exe" >/dev/null 2>&1
end=$(date +%s%N)
echo $(( (end - start)/1000000 ))
}
# Build once and time executable runs (ms)
time_exe_run() {
local exe="$1"
local start end
start=$(date +%s%N)
"$exe" >/dev/null 2>&1
end=$(date +%s%N)
echo $(( (end - start)/1000000 ))
}
mktemp_hako() { mktemp --suffix .hako; }
mktemp_c() { mktemp --suffix .c; }
case "$CASE" in
loop)
HAKO_FILE=$(mktemp_hako)
cat >"$HAKO_FILE" <<HAKO
static box Main { method main(args) {
local n = ${N}
local i = 0
local s = 0
loop(i < n) { s = s + i i = i + 1 }
return s
} }
HAKO
C_FILE=$(mktemp_c)
cat >"$C_FILE" <<'C'
#include <stdint.h>
int main(){
volatile int64_t n = N_PLACEHOLDER;
volatile int64_t s=0; for (int64_t i=0;i<n;i++){ s+=i; }
return (int)(s&0xFF);
}
C
sed -i "s/N_PLACEHOLDER/${N}/" "$C_FILE"
;;
strlen)
HAKO_FILE=$(mktemp_hako)
cat >"$HAKO_FILE" <<HAKO
static box Main { method main(args) {
local n = ${N}
local i = 0
local s = 0
local t = "abcdefghijklmnopqrstuvwxyz"
loop(i < n) { s = s + t.length() i = i + 1 }
return s
} }
HAKO
C_FILE=$(mktemp_c)
cat >"$C_FILE" <<'C'
#include <stdint.h>
#include <string.h>
int main(){
volatile int64_t n = N_PLACEHOLDER; volatile int64_t s=0;
const char* t = "abcdefghijklmnopqrstuvwxyz";
for (int64_t i=0;i<n;i++){ s += (int64_t)strlen(t); }
return (int)(s&0xFF);
}
C
sed -i "s/N_PLACEHOLDER/${N}/" "$C_FILE"
;;
box)
HAKO_FILE=$(mktemp_hako)
cat >"$HAKO_FILE" <<HAKO
static box Main { method main(args) {
local n = ${N}
local i = 0
loop(i < n) { local t = new StringBox("x"); i = i + 1 }
return 0
} }
HAKO
C_FILE=$(mktemp_c)
cat >"$C_FILE" <<'C'
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef struct { char* p; } Str;
static inline Str* new_str(){ Str* s=(Str*)malloc(sizeof(Str)); s->p=strdup("x"); free(s->p); free(s); return s; }
int main(){ volatile int64_t n=N_PLACEHOLDER; for(int64_t i=0;i<n;i++){ new_str(); } return 0; }
C
sed -i "s/N_PLACEHOLDER/${N}/" "$C_FILE"
;;
*) echo "Unknown case: $CASE"; exit 2;;
esac
echo "[perf] case=$CASE n=$N runs=$RUNS backend=$BACKEND" >&2
sum_c=0; sum_h=0
if [[ "$EXE_MODE" = "1" ]]; then
# Build C exe once
C_EXE=$(mktemp --suffix .out)
cc -O3 -march=native -o "$C_EXE" "$C_FILE"
# Build Nyash exe once (requires llvm harness)
if [[ "$BACKEND" != "llvm" ]]; then
echo "[FAIL] --exe requires --backend llvm" >&2; exit 2
fi
if [[ ! -x "$ROOT/target/release/ny-llvmc" ]]; then
(cargo build -q --release -p nyash-llvm-compiler >/dev/null 2>&1) || true
fi
HAKO_EXE=$(mktemp --suffix .out)
TMP_JSON=$(mktemp --suffix .json)
if ! HAKO_SELFHOST_BUILDER_FIRST=1 HAKO_MIR_BUILDER_LOOP_JSONFRAG=1 HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1 \
NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 \
NYASH_JSON_ONLY=1 bash "$ROOT/tools/hakorune_emit_mir.sh" "$HAKO_FILE" "$TMP_JSON" >/dev/null 2>&1; then
echo "[FAIL] failed to emit MIR JSON" >&2; exit 3
fi
# Ensure runtime lib exists (nyash_kernel)
(cd "$ROOT/crates/nyash_kernel" && cargo build -q --release >/dev/null) || true
# Build EXE via helper (selects crate backend ny-llvmc under the hood)
if ! NYASH_LLVM_BACKEND=crate \
NYASH_NY_LLVM_COMPILER="${NYASH_NY_LLVM_COMPILER:-$ROOT/target/release/ny-llvmc}" \
NYASH_EMIT_EXE_NYRT="${NYASH_EMIT_EXE_NYRT:-$ROOT/target/release}" \
NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 NYASH_LLVM_FAST=1 \
bash "$ROOT/tools/ny_mir_builder.sh" --in "$TMP_JSON" --emit exe -o "$HAKO_EXE" --quiet >/dev/null 2>&1; then
echo "[FAIL] failed to build Nyash EXE" >&2; exit 3
fi
for i in $(seq 1 "$RUNS"); do
t_c=$(time_exe_run "$C_EXE")
t_h=$(time_exe_run "$HAKO_EXE")
sum_c=$((sum_c + t_c)); sum_h=$((sum_h + t_h))
if command -v python3 >/dev/null 2>&1; then
ratio=$(python3 -c "print(round(${t_h}/max(${t_c},1)*100,2))" 2>/dev/null || echo NA)
else
ratio=NA
fi
echo "run#$i c=${t_c}ms hak=${t_h}ms ratio=${ratio}%" >&2
done
avg_c=$((sum_c / RUNS)); avg_h=$((sum_h / RUNS))
echo "avg c=${avg_c}ms hak=${avg_h}ms" >&2
if command -v python3 >/dev/null 2>&1; then
python3 - <<PY
c=$avg_c; h=$avg_h
ratio = (h/max(c,1))*100.0
print(f"ratio={ratio:.2f}%")
PY
fi
rm -f "$C_EXE" "$HAKO_EXE" "$TMP_JSON" 2>/dev/null || true
else
for i in $(seq 1 "$RUNS"); do
t_c=$(bench_c "$C_FILE" "${C_FILE%.c}")
t_h=$(bench_hako "$HAKO_FILE" "$BACKEND")
sum_c=$((sum_c + t_c)); sum_h=$((sum_h + t_h))
if command -v python3 >/dev/null 2>&1; then
ratio=$(python3 -c "print(round(${t_h}/max(${t_c},1)*100,2))" 2>/dev/null || echo NA)
else
ratio=NA
fi
echo "run#$i c=${t_c}ms hak=${t_h}ms ratio=${ratio}%" >&2
done
avg_c=$((sum_c / RUNS)); avg_h=$((sum_h / RUNS))
echo "avg c=${avg_c}ms hak=${avg_h}ms" >&2
if command -v python3 >/dev/null 2>&1; then
python3 - <<PY
c=$avg_c; h=$avg_h
ratio = (h/max(c,1))*100.0
print(f"ratio={ratio:.2f}%")
PY
fi
fi
rm -f "$HAKO_FILE" "$C_FILE" "${C_FILE%.c}" 2>/dev/null || true

View File

@ -0,0 +1,47 @@
#!/usr/bin/env bash
# selfhost_exe_stageb.sh — StageB → MirBuilder → nyllvmc (crate) → EXE
# Purpose: Build a native EXE from a Nyash .hako source using StageB+MirBuilder (selfhostfirst)
# Usage: tools/selfhost_exe_stageb.sh <input.hako> [-o <out>] [--run]
set -euo pipefail
OUT="a.out"; DO_RUN=0
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <input.hako> [-o <out>] [--run]" >&2; exit 2
fi
INPUT=""
while [[ $# -gt 0 ]]; do
case "$1" in
-o) OUT="$2"; shift 2;;
--run) DO_RUN=1; shift;;
*) INPUT="$1"; shift;;
esac
done
if [[ ! -f "$INPUT" ]]; then echo "error: input not found: $INPUT" >&2; exit 2; fi
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
# 1) Emit MIR(JSON) via StageB → MirBuilder (selfhostfirst)
TMP_JSON=$(mktemp --suffix .json)
HAKO_SELFHOST_BUILDER_FIRST=1 \
HAKO_MIR_BUILDER_LOOP_JSONFRAG=1 \
HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1 \
NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 \
NYASH_JSON_ONLY=1 "$ROOT_DIR/tools/hakorune_emit_mir.sh" "$INPUT" "$TMP_JSON" >/dev/null
echo "[emit] MIR JSON: $TMP_JSON ($(wc -c < "$TMP_JSON") bytes)"
# 2) Build EXE via crate backend (ny-llvmc) using helper
NYASH_LLVM_BACKEND=crate \
NYASH_NY_LLVM_COMPILER="${NYASH_NY_LLVM_COMPILER:-$ROOT_DIR/target/release/ny-llvmc}" \
NYASH_EMIT_EXE_NYRT="${NYASH_EMIT_EXE_NYRT:-$ROOT_DIR/target/release}" \
"$ROOT_DIR/tools/ny_mir_builder.sh" --in "$TMP_JSON" --emit exe -o "$OUT" --quiet >/dev/null
echo "[link] EXE: $OUT"
if [[ "$DO_RUN" = "1" ]]; then
set +e
"$OUT"; rc=$?
set -e
echo "[run] exit=$rc"
fi
exit 0

View File

@ -1,29 +1,24 @@
Smokes v2 — Quick vs Optional Smokes v2 — Minimal Runner and Policy
Purpose Policy
- Keep an alwayson, fast “quick” subset to sanitycheck core paths every time. - Use [SKIP:<reason>] prefix for environment/host dependent skips.
- Keep broader/experimental/hostdependent reps as optional, SKIPguarded. - Examples: [SKIP] hakorune not built, [SKIP:env] plugin path missing
- Keep reasons short and stable to allow grep-based canaries.
- Prefer JSON-only output in CI: set `NYASH_JSON_ONLY=1` to avoid noisy logs.
- Diagnostics lines like `[provider/select:*]` are filtered by default in `lib/test_runner.sh`.
Alwayson (quick) Helpers
- Runner: `tools/smokes/v2/run_quick.sh` - `tools/smokes/v2/lib/mir_canary.sh` provides:
- Includes: - `extract_mir_from_output` — between [MIR_BEGIN]/[MIR_END]
- Core crate/native reps (phase2100/run_all.sh) — SKIP when unavailable - `assert_has_tokens`, `assert_skip_tag`, `assert_order`, `assert_token_count`
- StageB Program(JSON) shape (phase2160: stageb_* canaries) - `tools/lib/canary.sh` provides minimal, harness-agnostic aliases:
- loop_scan minimal canaries (!=' + else Break/Continue) - `extract_mir_between_tags` — same as `extract_mir_from_output`
- `require_tokens token...` — fail if any token missing
Optional (perphase) Notes
- Richer/experimental reps remain under `profiles/quick/core/phase*/run_all.sh` and individual canaries. - Avoid running heavy integration smokes in CI by default. Use `--profile quick`.
- Examples: - When a test depends on external tools (e.g., LLVM), prefer `[SKIP:<reason>]` over failure.
- registry_* tag observation canaries (structure only)
- program_to_mir_exe_* (host/tooling dependent, SKIPguarded)
Conventions
- 3state outcomes: PASS / SKIP / FAILFAIL should be rare and intentional
- SKIP guards are preferred over brittle environment assumptions.
- Add light canaries first; escalate to run_all only when stable and fast.
Usage
- Run quick set: `bash tools/smokes/v2/run_quick.sh`
- Run a phase: `bash tools/smokes/v2/profiles/quick/core/<phase>/run_all.sh`
- Run a single canary: `bash tools/smokes/v2/profiles/quick/core/<phase>/<name>_canary_vm.sh`
Quick tips
- EXE-heavy cases (e.g., `phase2100/*`) may take longer. When running quick with these tests, pass a larger timeout like `--timeout 120`.
- Smokes v2 auto-cleans temporary crate EXE objects created under `/tmp` (pattern: `ny_crate_backend_exe_*.o`) after the run.

View File

@ -856,3 +856,18 @@ enable_mirbuilder_dev_env() {
# export HAKO_MIR_BUILDER_NORMALIZE_TAG=1 # optional: show tags in logs for diagnostics # export HAKO_MIR_BUILDER_NORMALIZE_TAG=1 # optional: show tags in logs for diagnostics
# fi # fi
} }
# Dev profile helpers (EXE/AOT bring-up)
# Sets environment defaults for LLVM crate backend and EXE link paths.
# Usage: call enable_exe_dev_env in EXE canaries.
enable_exe_dev_env() {
# Prefer crate backend when available
export NYASH_LLVM_BACKEND=${NYASH_LLVM_BACKEND:-crate}
# Tool locations (override when cross)
export NYASH_NY_LLVM_COMPILER=${NYASH_NY_LLVM_COMPILER:-"$NYASH_ROOT/target/release/ny-llvmc"}
# NyRT (kernel) lib search path for linking EXEs
export NYASH_EMIT_EXE_NYRT=${NYASH_EMIT_EXE_NYRT:-"$NYASH_ROOT/target/release"}
# Optional verification toggles (kept ON by default only for canaries that opt-in)
export NYASH_LLVM_VERIFY=${NYASH_LLVM_VERIFY:-0}
export NYASH_LLVM_VERIFY_IR=${NYASH_LLVM_VERIFY_IR:-0}
}

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
# Prebuild required tools/libraries # Prebuild required tools/libraries
@ -27,7 +28,10 @@ APP="/tmp/ny_crate_backend_exe_binop_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_binop_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_binop_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_and_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_and_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_and_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_bitwise_and_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_bitwise_and_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_cmp_eq_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_cmp_eq_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_cmp_eq_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_compare_eq_true_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_compare_eq_true_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_cmp_ge_b_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_cmp_ge_b_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_cmp_ge_b_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_compare_ge_boundary_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_compare_ge_boundary_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_cmp_le_b_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_cmp_le_b_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_cmp_le_b_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_compare_le_boundary_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_compare_le_boundary_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_cmp_lt_neg_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_cmp_lt_neg_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_cmp_lt_neg_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_compare_lt_neg_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_compare_lt_neg_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_cmp_ne_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_cmp_ne_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_cmp_ne_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_compare_ne_true_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_compare_ne_true_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_div_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_div_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_div_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_div_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_div_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -34,7 +35,10 @@ APP="/tmp/ny_crate_backend_exe_phi_ret42_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_phi_ret42_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_phi_ret42_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -49,4 +53,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_phi_branch_return42_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_phi_branch_return42_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
# Prebuild required tools/libraries # Prebuild required tools/libraries
@ -10,6 +11,8 @@ BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
# Build minimal C runtime (design-stage) to provide nyash_console_log # Build minimal C runtime (design-stage) to provide nyash_console_log
(cd "$ROOT" && cargo build -q --release -p nyash-kernel-min-c >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-kernel-min-c >/dev/null) || true
enable_exe_dev_env
# Minimal MIR v1 JSON that intends to print then return 0. # Minimal MIR v1 JSON that intends to print then return 0.
# Note: If the builder rejects schema, we SKIP gracefully. # Note: If the builder rejects schema, we SKIP gracefully.
JSON='{ JSON='{
@ -32,7 +35,8 @@ echo "$JSON" > "$TMP_JSON"
LIBDIR_MIN="$ROOT/target/release" LIBDIR_MIN="$ROOT/target/release"
LIBS="-L $LIBDIR_MIN -lnyash_kernel_min_c" LIBS="-L $LIBDIR_MIN -lnyash_kernel_min_c"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --libs="$LIBS" --out "$APP" >/dev/null 2>&1; then if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --libs="$LIBS" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
out="$($APP 2>/dev/null)"; rc=$? out="$($APP 2>/dev/null)"; rc=$?

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_rem_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_rem_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_rem_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_rem_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_rem_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,11 +2,13 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
# Prebuild required tools/libraries # Prebuild required tools/libraries
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
(cd "$ROOT/crates/nyash_kernel" && cargo build -q --release >/dev/null) || true (cd "$ROOT/crates/nyash_kernel" && cargo build -q --release >/dev/null) || true
enable_exe_dev_env
# Minimal MIR v1 JSON that returns 42. # Minimal MIR v1 JSON that returns 42.
JSON='{ JSON='{
@ -25,7 +27,8 @@ APP="/tmp/ny_crate_backend_exe_ret42_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_ret42_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_ret42_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -37,7 +40,6 @@ if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyr
fi fi
fi fi
fi fi
echo "[SKIP] s3_backend_selector_crate_exe_return42_canary_vm (rc mapping not active)" >&2 echo "[FAIL] s3_backend_selector_crate_exe_return42_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 0 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
# Prebuild required tools/libraries # Prebuild required tools/libraries
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_ret_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_ret_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_ret_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_return_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_return_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_shl_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_shl_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_shl_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_shift_left_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_shift_left_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -2,6 +2,7 @@
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)" ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
@ -25,7 +26,10 @@ APP="/tmp/ny_crate_backend_exe_shr_$$"
TMP_JSON="/tmp/ny_crate_backend_exe_shr_$$.json" TMP_JSON="/tmp/ny_crate_backend_exe_shr_$$.json"
echo "$JSON" > "$TMP_JSON" echo "$JSON" > "$TMP_JSON"
if HAKO_LLVM_CANARY_NORMALIZE=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then enable_exe_dev_env
if NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 HAKO_LLVM_CANARY_NORMALIZE=1 \
"$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$APP" >/dev/null 2>&1; then
if [[ -x "$APP" ]]; then if [[ -x "$APP" ]]; then
set +e set +e
"$APP" >/dev/null 2>&1; rc=$? "$APP" >/dev/null 2>&1; rc=$?
@ -40,4 +44,3 @@ fi
echo "[FAIL] s3_backend_selector_crate_exe_shift_right_canary_vm" >&2 echo "[FAIL] s3_backend_selector_crate_exe_shift_right_canary_vm" >&2
rm -f "$APP" "$TMP_JSON" 2>/dev/null || true rm -f "$APP" "$TMP_JSON" 2>/dev/null || true
exit 1 exit 1

View File

@ -3,9 +3,12 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/../../../../../../.." && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/../../../../../../.." && pwd)"
source "$ROOT_DIR/tools/smokes/v2/lib/test_runner.sh" || true
BIN_NYLLVMC="$ROOT_DIR/target/release/ny-llvmc" BIN_NYLLVMC="$ROOT_DIR/target/release/ny-llvmc"
BIN_HAKO="$ROOT_DIR/target/release/hakorune" BIN_HAKO="$ROOT_DIR/target/release/hakorune"
enable_exe_dev_env
# Build tools if missing # Build tools if missing
(cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true (cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
(cargo build -q --release >/dev/null) || true (cargo build -q --release >/dev/null) || true
@ -30,7 +33,8 @@ fi
set -e set -e
# Build exe with FAST lowering ON # Build exe with FAST lowering ON
if ! NYASH_LLVM_FAST=1 bash "$ROOT_DIR/tools/ny_mir_builder.sh" --in "$TMP_JSON" --emit exe -o "$EXE_OUT" --quiet >/dev/null 2>&1; then if ! NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 NYASH_LLVM_FAST=1 \
bash "$ROOT_DIR/tools/ny_mir_builder.sh" --in "$TMP_JSON" --emit exe -o "$EXE_OUT" --quiet >/dev/null 2>&1; then
echo "[SKIP] failed to build EXE"; exit 0 echo "[SKIP] failed to build EXE"; exit 0
fi fi

View File

@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
# Ensure tools are built and environment is consistent for EXE
(cd "$ROOT" && cargo build -q --release -p nyash-llvm-compiler >/dev/null) || true
(cd "$ROOT/crates/nyash_kernel" && cargo build -q --release >/dev/null) || true
enable_exe_dev_env
# Minimal Hako program returning 42
TMP_HAKO=$(mktemp --suffix .hako)
cat >"$TMP_HAKO" <<'HAKO'
static box Main { method main(args) { return 42 } }
HAKO
TMP_JSON=$(mktemp --suffix .json)
EXE_OUT="${ROOT}/target/parity_ret42_$$"
BIN_NYLLVMC="$ROOT/target/release/ny-llvmc"
trap 'rm -f "$TMP_HAKO" "$TMP_JSON" "$EXE_OUT" 2>/dev/null || true' EXIT
# Run via VM and capture program exit code from return status
set +e
run_nyash_vm "$TMP_HAKO" >/dev/null 2>&1
rc_vm=$?
set -e
# Emit MIR JSON and build EXE (crate backend)
if ! NYASH_JSON_ONLY=1 bash "$ROOT/tools/hakorune_emit_mir.sh" "$TMP_HAKO" "$TMP_JSON" >/dev/null 2>&1; then
echo "[FAIL] exe_vm_parity_ret42: failed to emit MIR JSON" >&2
exit 1
fi
if ! NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 "$BIN_NYLLVMC" --in "$TMP_JSON" --emit exe --nyrt "$ROOT/target/release" --out "$EXE_OUT" >/dev/null 2>&1; then
echo "[FAIL] exe_vm_parity_ret42: failed to build EXE" >&2
exit 1
fi
# Run EXE and compare RCs
set +e
"$EXE_OUT" >/dev/null 2>&1
rc_exe=$?
set -e
if [[ "$rc_exe" -eq "$rc_vm" ]]; then
echo "[PASS] s3_backend_selector_crate_exe_vm_parity_return42_canary_vm"
exit 0
fi
echo "[FAIL] exe_vm_parity_ret42: mismatch vm=$rc_vm exe=$rc_exe" >&2
exit 1

View File

@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
enable_exe_dev_env
# Minimal loop program (structure only)
TMP_HAKO=$(mktemp --suffix .hako)
cat >"$TMP_HAKO" <<'HAKO'
static box Main { method main(args){
local n=10; local i=0;
loop(i<n){ i=i+1 }
return i
} }
HAKO
TMP_JSON=$(mktemp --suffix .json)
EXE_OUT="${ROOT}/target/stageb_loop_jsonfrag_$$"
trap 'rm -f "$TMP_HAKO" "$TMP_JSON" "$EXE_OUT" 2>/dev/null || true' EXIT
# Emit MIR(JSON) via selfhost-first and JSONFrag loop (normalized)
if ! HAKO_SELFHOST_BUILDER_FIRST=1 HAKO_MIR_BUILDER_LOOP_JSONFRAG=1 HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1 HAKO_MIR_BUILDER_JSONFRAG_PURIFY=1 HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG=1 \
NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 \
NYASH_JSON_ONLY=1 bash "$ROOT/tools/hakorune_emit_mir.sh" "$TMP_HAKO" "$TMP_JSON" >/dev/null 2>&1; then
echo "[FAIL] stageb_loop_jsonfrag: failed to emit MIR JSON"; exit 1
fi
# Assert no MapBox/newbox present in MIR(JSON)
if rg -n "newbox|MapBox" "$TMP_JSON" >/dev/null 2>&1; then
echo "[FAIL] stageb_loop_jsonfrag: found MapBox/newbox in MIR"; exit 1
fi
# Build EXE via crate backend
if ! NYASH_LLVM_BACKEND=crate NYASH_LLVM_VERIFY=1 NYASH_LLVM_VERIFY_IR=1 \
NYASH_NY_LLVM_COMPILER="${NYASH_NY_LLVM_COMPILER:-$ROOT/target/release/ny-llvmc}" \
NYASH_EMIT_EXE_NYRT="${NYASH_EMIT_EXE_NYRT:-$ROOT/target/release}" \
bash "$ROOT/tools/ny_mir_builder.sh" --in "$TMP_JSON" --emit exe -o "$EXE_OUT" --quiet >/dev/null 2>&1; then
echo "[FAIL] stageb_loop_jsonfrag: failed to build EXE"; exit 1
fi
# Run and just ensure it executes (RC arbitrary here because structure-only lower)
set +e
"$EXE_OUT" >/dev/null 2>&1
rc=$?
set -e
if [[ "$rc" -ge 0 ]]; then
echo "[PASS] stageb_loop_jsonfrag_crate_exe_canary_vm"
exit 0
fi
echo "[FAIL] stageb_loop_jsonfrag_crate_exe_canary_vm (unexpected run failure)"; exit 1

View File

@ -428,6 +428,9 @@ EOF
;; ;;
esac esac
# Post-run cleanup: remove smokes-generated crate EXE object files under /tmp
rm -f /tmp/ny_crate_backend_exe_*.o 2>/dev/null || true
# 終了コード # 終了コード
[ $failed -eq 0 ] [ $failed -eq 0 ]
} }