## 修正内容 ### 1. Stage-B パーサー修正(偶然の回避) - **ファイル**: - `lang/src/compiler/parser/expr/parser_expr_box.hako` - `lang/src/compiler/parser/stmt/parser_control_box.hako` - **問題**: ネストループで gpos が正しく進まず、loop の cond/body が壊れる - **回避策**: new 式のメソッドチェーン処理追加で別ループを導入 - **結果**: MIR 生成が変わって VM gpos バグを回避 ### 2. delegate パス動作確認 - **テスト**: `/tmp/loop_min.hako` → rc=10 ✅ - **MIR構造**: 正しい PHI/compare/binop を生成 - **チェーン**: hakorune parser → Rust delegate → LLVM EXE 完動 ### 3. ドキュメント追加 - `docs/development/analysis/` - delegate 分析 - `docs/development/guides/` - ループテストガイド - `docs/development/testing/` - Stage-B 検証報告 ### 4. カナリーテスト追加 - `tools/smokes/v2/profiles/quick/core/phase2100/` 配下に複数追加 - emit_boxcall_length_canary_vm.sh - stageb_parser_loop_json_canary_vm.sh - 他 ### 受け入れ基準 - ✅ delegate パス: rc=10 返す - ✅ FORCE パス: rc=10 返す(既存) - ✅ MIR 構造: 正しい PHI incoming と compare - ✅ 既定挙動: 不変(dev トグルのみ) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
218 lines
6.6 KiB
Bash
218 lines
6.6 KiB
Bash
#!/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_LOOP_FORCE_JSONFRAG=1 \
|
|
HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1 HAKO_MIR_BUILDER_JSONFRAG_PURIFY=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
|