Phase 21.7 normalization: optimization pre-work + bench harness expansion

- Add opt-in optimizations (defaults OFF)
  - Ret purity verifier: NYASH_VERIFY_RET_PURITY=1
  - strlen FAST enhancement for const handles
  - FAST_INT gate for same-BB SSA optimization
  - length cache for string literals in llvmlite
- Expand bench harness (tools/perf/microbench.sh)
  - Add branch/call/stringchain/arraymap/chip8/kilo cases
  - Auto-calculate ratio vs C reference
  - Document in benchmarks/README.md
- Compiler health improvements
  - Unify PHI insertion to insert_phi_at_head()
  - Add NYASH_LLVM_SKIP_BUILD=1 for build reuse
- Runtime & safety enhancements
  - Clarify Rust/Hako ownership boundaries
  - Strengthen receiver localization (LocalSSA/pin/after-PHIs)
  - Stop excessive PluginInvoke→BoxCall rewrites
- Update CURRENT_TASK.md, docs, and canaries

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-13 16:40:58 +09:00
parent 9e2fa1e36e
commit dda65b94b7
160 changed files with 6773 additions and 1692 deletions

View File

@ -44,6 +44,7 @@ CODE="$(cat "$IN")"
# 1) StageB: Hako parser emits Program(JSON v0) to stdout
set +e
PROG_JSON_OUT=$(NYASH_JSON_ONLY=1 NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-}" \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
NYASH_ENABLE_USING=${NYASH_ENABLE_USING:-1} HAKO_ENABLE_USING=${HAKO_ENABLE_USING:-1} \
"$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$CODE" 2>/dev/null | awk '/^{/,/^}$/')
@ -117,6 +118,53 @@ try_selfhost_builder() {
}
MIRJSON
# Provider-first delegate: call env.mirbuilder.emit(prog_json) and capture v1 JSON
try_provider_emit() {
local prog_json="$1" out_path="$2"
local tmp_hako; tmp_hako=$(mktemp --suffix .hako)
cat >"$tmp_hako" <<'HCODE'
using "hako.mir.builder.internal.jsonfrag_normalizer" as NormBox
static box Main { method main(args) {
local p = env.get("HAKO_BUILDER_PROGRAM_JSON")
if p == null { print("[provider/emit:nojson]"); return 1 }
local a = new ArrayBox(); a.push(p)
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", a)
// Optional normalization (dev): apply JsonFrag normalizer/purifier to provider output
{
local nv = env.get("HAKO_MIR_NORMALIZE_PROVIDER")
if nv != null && ("" + nv) == "1" {
local out_s = "" + out
out = NormBox.normalize_all(out_s)
}
}
print("[provider/emit:ok]")
print("[MIR_OUT_BEGIN]")
print("" + out)
print("[MIR_OUT_END]")
return 0
} }
HCODE
local tmp_stdout; tmp_stdout=$(mktemp)
trap 'rm -f "$tmp_hako" "$tmp_stdout" || true' RETURN
set +e
(cd "$ROOT" && \
NYASH_DISABLE_PLUGINS=1 NYASH_FILEBOX_MODE="core-ro" \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
HAKO_BUILDER_PROGRAM_JSON="$prog_json" \
"$NYASH_BIN" --backend vm "$tmp_hako" 2>&1 | tee "$tmp_stdout" >/dev/null)
local rc=$?
set -e
if [ $rc -ne 0 ] || ! grep -q "\[provider/emit:ok\]" "$tmp_stdout"; then
return 1
fi
local mir
mir=$(awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' "$tmp_stdout")
if [ -z "$mir" ]; then return 1; fi
printf '%s' "$mir" > "$out_path"
echo "[OK] MIR JSON written (delegate:provider): $out_path"
return 0
}
# Replace LIMIT_PLACEHOLDER with actual limit
sed -i "s/LIMIT_PLACEHOLDER/$limit/g" "$out_path"
@ -131,7 +179,23 @@ MIRJSON
local builder_box="${HAKO_MIR_BUILDER_BOX:-hako.mir.builder}"
local tmp_hako; tmp_hako=$(mktemp --suffix .hako)
cat >"$tmp_hako" <<'HCODE'
if [ "$builder_box" = "hako.mir.builder.min" ]; then
cat >"$tmp_hako" <<'HCODE'
using "hako.mir.builder.internal.runner_min" as BuilderRunnerMinBox
static box Main { method main(args) {
local prog_json = env.get("HAKO_BUILDER_PROGRAM_JSON")
if prog_json == null { print("[builder/selfhost-first:fail:nojson]"); return 1 }
local mir_out = BuilderRunnerMinBox.run(prog_json)
if mir_out == null { print("[builder/selfhost-first:fail:emit]"); return 1 }
print("[builder/selfhost-first:ok]")
print("[MIR_OUT_BEGIN]")
print("" + mir_out)
print("[MIR_OUT_END]")
return 0
} }
HCODE
else
cat >"$tmp_hako" <<'HCODE'
using "__BUILDER_BOX__" as MirBuilderBox
static box Main { method main(args) {
local prog_json = env.get("HAKO_BUILDER_PROGRAM_JSON")
@ -145,8 +209,8 @@ static box Main { method main(args) {
return 0
} }
HCODE
# Substitute builder box name after heredoc to avoid shell interpolation issues
sed -i "s|__BUILDER_BOX__|$builder_box|g" "$tmp_hako"
sed -i "s|__BUILDER_BOX__|$builder_box|g" "$tmp_hako"
fi
local tmp_stdout; tmp_stdout=$(mktemp)
trap 'rm -f "$tmp_hako" "$tmp_stdout" || true' RETURN
@ -175,9 +239,10 @@ HCODE
HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG="${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-}" \
HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE="${HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE:-}" \
HAKO_MIR_BUILDER_JSONFRAG_PURIFY="${HAKO_MIR_BUILDER_JSONFRAG_PURIFY:-}" \
HAKO_MIR_BUILDER_METHODIZE="${HAKO_MIR_BUILDER_METHODIZE:-}" \
HAKO_MIR_BUILDER_NORMALIZE_TAG="${HAKO_MIR_BUILDER_NORMALIZE_TAG:-}" \
HAKO_MIR_BUILDER_DEBUG="${HAKO_MIR_BUILDER_DEBUG:-}" \
NYASH_DISABLE_PLUGINS=1 NYASH_FILEBOX_MODE="core-ro" HAKO_PROVIDER_POLICY="safe-core-first" \
NYASH_DISABLE_PLUGINS="${NYASH_DISABLE_PLUGINS:-0}" NYASH_FILEBOX_MODE="core-ro" HAKO_PROVIDER_POLICY="safe-core-first" \
NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
NYASH_USE_NY_COMPILER=0 HAKO_USE_NY_COMPILER=0 NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
@ -230,6 +295,44 @@ HCODE
return 0
}
# Provider-first delegate: call env.mirbuilder.emit(prog_json) and capture v1 JSON
try_provider_emit() {
local prog_json="$1" out_path="$2"
local tmp_hako; tmp_hako=$(mktemp --suffix .hako)
cat >"$tmp_hako" <<'HCODE'
static box Main { method main(args) {
local p = env.get("HAKO_BUILDER_PROGRAM_JSON")
if p == null { print("[provider/emit:nojson]"); return 1 }
local a = new ArrayBox(); a.push(p)
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", a)
print("[provider/emit:ok]")
print("[MIR_OUT_BEGIN]")
print("" + out)
print("[MIR_OUT_END]")
return 0
} }
HCODE
local tmp_stdout; tmp_stdout=$(mktemp)
trap 'rm -f "$tmp_hako" "$tmp_stdout" || true' RETURN
set +e
(cd "$ROOT" && \
NYASH_DISABLE_PLUGINS="${NYASH_DISABLE_PLUGINS:-0}" NYASH_FILEBOX_MODE="core-ro" \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
HAKO_BUILDER_PROGRAM_JSON="$prog_json" \
"$NYASH_BIN" --backend vm "$tmp_hako" 2>&1 | tee "$tmp_stdout" >/dev/null)
local rc=$?
set -e
if [ $rc -ne 0 ] || ! grep -q "\[provider/emit:ok\]" "$tmp_stdout"; then
return 1
fi
local mir
mir=$(awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' "$tmp_stdout")
if [ -z "$mir" ]; then return 1; fi
printf '%s' "$mir" > "$out_path"
echo "[OK] MIR JSON written (delegate:provider): $out_path"
return 0
}
# When forcing JSONFrag loop, default-enable normalize+purify (dev-only, no default changes)
if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then
export HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE="${HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE:-1}"
@ -246,12 +349,61 @@ if [ "${HAKO_SELFHOST_BUILDER_FIRST:-0}" = "1" ]; then
fi
fi
# Dev: force JsonFrag minimal loop even on provider-first path
if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then
# Extract limit from Program(JSON)
limit=$(printf '%s' "$PROG_JSON_OUT" | grep -o '"type":"Int","value":[0-9]*' | head -1 | grep -o '[0-9]*$' || echo "10")
cat > "$OUT" <<MIRJSON
{
"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": ${limit} }},
{"op":"jump","target":1}
]},
{ "id": 1, "instructions": [
{"op":"phi","dst":6,"incoming":[[2,0],[6,2]]},
{"op":"phi","dst":3,"incoming":[[1,0],[5,2]]},
{"op":"compare","operation":"<","lhs":3,"rhs":6,"dst":4},
{"op":"branch","cond":4,"then":2,"else":3}
]},
{ "id": 2, "instructions": [
{"op":"const","dst":10,"value":{"type":"i64","value":1}},
{"op":"binop","operation":"+","lhs":3,"rhs":10,"dst":5},
{"op":"jump","target":1}
]},
{ "id": 3, "instructions": [
{"op":"ret","value":3}
]}
]
}]
}
MIRJSON
echo "[OK] MIR JSON written (provider-force-jsonfrag): $OUT"
exit 0
fi
tmp_prog="/tmp/hako_emit_prog_$$.json"
trap 'rm -f "$tmp_prog" || true' EXIT
printf '%s' "$PROG_JSON_OUT" > "$tmp_prog"
if "$NYASH_BIN" --program-json-to-mir "$OUT" --json-file "$tmp_prog" >/dev/null 2>&1; then
echo "[OK] MIR JSON written (delegate): $OUT"
# Provider-first delegate (v1固定): env.mirbuilder.emit を使用
if try_provider_emit "$PROG_JSON_OUT" "$OUT"; then
exit 0
fi
echo "[FAIL] Program→MIR delegate failed" >&2
# 最終フォールバック: 旧CLI変換環境でv1を促す
if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-}" \
HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \
HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \
NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \
NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \
"$NYASH_BIN" --program-json-to-mir "$OUT" --json-file "$tmp_prog" >/dev/null 2>&1; then
echo "[OK] MIR JSON written (delegate-legacy): $OUT"
exit 0
fi
echo "[FAIL] Program→MIR delegate failed (provider+legacy)" >&2
exit 1