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

@ -52,6 +52,18 @@ static box MirBuilderBox {
local internal = env.get("HAKO_MIR_BUILDER_INTERNAL")
local internal_on = (internal == null) || (("" + internal) == "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,
// iterate PatternRegistryBox.candidates() and dispatch by name.
// NOTE: using/alias は prelude で解決される(位置に依存しない)。

View File

@ -1,22 +1,12 @@
# MirBuilder Internal Boxes — Minimal Shapes (Phase 20.34)
MirBuilder Internals — Toggle Aggregation
Responsibility
- Provide small, readable boxes that lower a very small subset of Program(JSON v0)
into MIR(JSON v0) strings without heavy parsing. FailFast with stable tags.
- Use `builder_config_box.hako` (`hako.mir.builder.internal.builder_config`) to read all `HAKO_MIR_BUILDER_*` toggles.
- Do not call `env.get` directly in lowers; prefer helper methods like:
- `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
- `prog_scan_box.hako` — tiny helpers for string scanning: find, skip spaces, read quoted/op/int values.
- `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", ...)。
Notes
- JsonFrag emission is kept default OFF and used for structural observation only. Semantics are prioritized by the normal path.
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 others_list = new ArrayBox()
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) {
guard = guard + 1
if guard > 4096 { break }
@ -52,6 +54,9 @@ static box JsonFragNormalizerBox {
i = ob_end + 1
// classify by op
local op = JsonFragBox.get_str(obj, "op")
if purify == 1 {
if op == "newbox" { continue }
}
if op == "phi" {
phi_list.push(obj)
continue

View File

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

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